rtp_engine: add support for multirate RFC2833 digits

Add RFC2833 DTMF support for 16K, 24K, and 32K bitrate codecs.

Asterisk currently treats RFC2833 Digits as a single rtp payload type
with a fixed bitrate of 8K.  This change would expand that to 8, 16,
24 and 32K.

This requires checking the offered rtp types for any of these bitrates
and then adding an offer for each (if configured for RFC2833.)  DTMF
generation must also be changed in order to look at the current outbound
codec in order to generate appropriately timed rtp.

For cases where no outgoing audio has yet been sent prior to digit
generation, Asterisk now has a concept of a 'preferred' codec based on
offer order.

On inbound calls Asterisk will mimic the payload types of the RFC2833
digits.

On outbound calls Asterisk will choose the next free payload types starting
with 101.

UserNote: No change in configuration is required in order to enable this
feature. Endpoints configured to use RFC2833 will automatically have this
enabled. If the endpoint does not support this, it should not include it in
the SDP offer/response.

Resolves: #699
(cherry picked from commit 182ea91fc5)
pull/886/head
Mike Bradeen 1 year ago committed by Asterisk Development Team
parent 14367caaf7
commit 728dbdccd1

@ -311,6 +311,8 @@ struct ast_rtp_payload_type {
unsigned int primary_mapping:1;
/*! When the payload type became non-primary. */
struct timeval when_retired;
/*! Sample rate to over-ride mime type defaults */
unsigned int sample_rate;
};
/* Common RTCP report types */
@ -757,10 +759,12 @@ struct ast_rtp_codecs {
AST_VECTOR(, struct ast_rtp_payload_type *) payload_mapping_tx;
/*! The framing for this media session */
unsigned int framing;
/*! The preferred format, as the mappings are numerically sorted */
struct ast_format *preferred_format;
};
#define AST_RTP_CODECS_NULL_INIT \
{ .codecs_lock = AST_RWLOCK_INIT_VALUE, .payload_mapping_rx = { 0, }, .payload_mapping_tx = { 0, }, .framing = 0, }
{ .codecs_lock = AST_RWLOCK_INIT_VALUE, .payload_mapping_rx = { 0, }, .payload_mapping_tx = { 0, }, .framing = 0, .preferred_format = NULL }
/*! Structure that represents the glue that binds an RTP instance to a channel */
struct ast_rtp_glue {
@ -1659,6 +1663,50 @@ enum ast_media_type ast_rtp_codecs_get_stream_type(struct ast_rtp_codecs *codecs
*/
struct ast_rtp_payload_type *ast_rtp_codecs_get_payload(struct ast_rtp_codecs *codecs, int payload);
/*!
* \brief Retrieve rx preferred format
*
* \param codecs Codecs structure to look in
*
* \return format information.
* \retval NULL if format does not exist.
*
* \note The format returned by this function has its reference count increased.
* Callers are responsible for decrementing the reference count.
*
* Example usage:
*
* \code
* struct ast_format *payload_format;
* payload_format = ast_rtp_codecs_get_preferred_format(&codecs);
* \endcode
*
* This looks up the preferred format on the codec
*/
struct ast_format *ast_rtp_codecs_get_preferred_format(struct ast_rtp_codecs *codecs);
/*!
* \brief Set the preferred format
*
* \param codecs Codecs structure to set the preferred format in
* \param format Preferred format to set.
*
* \return 0
*
* \note The format passed this function has its reference count increased. If an existing
* format is set, that format is replaced.
*
* Example usage:
*
* \code
* struct ast_format *preferred_format = ast_format_cap_get_format(joint, 0);
* ast_rtp_codecs_set_preferred_format(&codecs, preferred_format));
* \endcode
*
* This sets the first joint format as the preferred format.
*/
int ast_rtp_codecs_set_preferred_format(struct ast_rtp_codecs *codecs, struct ast_format *format);
/*!
* \brief Update the format associated with a tx payload type in a codecs structure
*
@ -1774,6 +1822,36 @@ void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, struct ast_fo
*/
int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_format, struct ast_format *format, int code);
/*!
* \brief Retrieve a rx mapped payload type based on whether it is an Asterisk format, the code and the sample rate.
*
* \param codecs Codecs structure to look in
* \param asterisk_format Non-zero if the given Asterisk format is present
* \param format Asterisk format to look for
* \param code The format to look for
* \param sample_rate Non-zero if we want to also match on sample rate.
*
* \details
* Find the currently assigned rx mapped payload type based on whether it
* is an Asterisk format or non-format code. If one is currently not
* assigned then create a rx payload type mapping.
*
* \return Numerical payload type
* \retval -1 if could not assign.
*
* Example usage:
*
* \code
* int payload = ast_rtp_codecs_payload_code_sample_rate(&codecs, 0, NULL, AST_RTP_DTMF, 8000);
* \endcode
*
* This looks for the numerical payload for a DTMF type with a sample rate of 8kHz in the codecs structure.
*
* \since 22.0.0
*/
int ast_rtp_codecs_payload_code_sample_rate(struct ast_rtp_codecs *codecs, int asterisk_format, struct ast_format *format, int code, unsigned int sample_rate);
/*!
* \brief Set a payload code for use with a specific Asterisk format
*
@ -1788,6 +1866,21 @@ int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_form
*/
int ast_rtp_codecs_payload_set_rx(struct ast_rtp_codecs *codecs, int code, struct ast_format *format);
/*!
* \brief Set a payload code with sample rate for use with a specific Asterisk format
*
* \param codecs Codecs structure to manipulate
* \param code The payload code
* \param format Asterisk format
* \param sample_rate Sample rate of the format, 0 to use the format's default
*
* \retval 0 Payload was set to the given format
* \retval -1 Payload was in use or could not be set
*
* \since 22.0.0
*/
int ast_rtp_codecs_payload_set_rx_sample_rate(struct ast_rtp_codecs *codecs, int code, struct ast_format *format, unsigned int sample_rate);
/*!
* \brief Retrieve a tx mapped payload type based on whether it is an Asterisk format and the code
* \since 14.0.0
@ -1802,6 +1895,21 @@ int ast_rtp_codecs_payload_set_rx(struct ast_rtp_codecs *codecs, int code, struc
*/
int ast_rtp_codecs_payload_code_tx(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code);
/*!
* \brief Retrieve a tx mapped payload type based on whether it is an Asterisk format and the code
* \since 22.0.0
*
* \param codecs Codecs structure to look in
* \param asterisk_format Non-zero if the given Asterisk format is present
* \param format Asterisk format to look for
* \param code The format to look for
* \param sample_rate The sample rate to look for, zero if we don't care
*
* \return Numerical payload type
* \retval -1 if not found.
*/
int ast_rtp_codecs_payload_code_tx_sample_rate(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code, unsigned int sample_rate);
/*!
* \brief Search for the tx payload type in the ast_rtp_codecs structure
*

@ -303,7 +303,7 @@ static void rtp_payload_type_dtor(void *obj)
}
static struct ast_rtp_payload_type *rtp_payload_type_alloc(struct ast_format *format,
int payload, int rtp_code, int primary_mapping)
int payload, int rtp_code, int primary_mapping, unsigned int sample_rate)
{
struct ast_rtp_payload_type *type = ao2_alloc_options(
sizeof(*type), rtp_payload_type_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
@ -317,13 +317,14 @@ static struct ast_rtp_payload_type *rtp_payload_type_alloc(struct ast_format *fo
type->payload = payload;
type->rtp_code = rtp_code;
type->primary_mapping = primary_mapping;
type->sample_rate = sample_rate;
return type;
}
struct ast_rtp_payload_type *ast_rtp_engine_alloc_payload_type(void)
{
return rtp_payload_type_alloc(NULL, 0, 0, 0);
return rtp_payload_type_alloc(NULL, 0, 0, 0, 0);
}
int ast_rtp_engine_register2(struct ast_rtp_engine *engine, struct ast_module *module)
@ -1009,6 +1010,8 @@ void ast_rtp_codecs_payloads_destroy(struct ast_rtp_codecs *codecs)
}
AST_VECTOR_FREE(&codecs->payload_mapping_tx);
ao2_t_cleanup(codecs->preferred_format, "destroying ast_rtp_codec preferred format");
ast_rwlock_destroy(&codecs->codecs_lock);
}
@ -1085,15 +1088,16 @@ static void payload_mapping_rx_clear_primary(struct ast_rtp_codecs *codecs, stru
/*!
* \internal
* \brief Put the new_type into the rx payload type mapping.
* \since 14.0.0
* \since 21.0.0
*
* \param codecs Codecs structure to put new_type into
* \param payload type position to replace.
* \param new_type RTP payload mapping object to store.
* \param replace Clear the primary flag
*
* \note It is assumed that codecs is write locked before calling.
*/
static void rtp_codecs_payload_replace_rx(struct ast_rtp_codecs *codecs, int payload, struct ast_rtp_payload_type *new_type)
static void rtp_codecs_payload_set_rx(struct ast_rtp_codecs *codecs, int payload, struct ast_rtp_payload_type *new_type, int replace)
{
ao2_ref(new_type, +1);
if (payload < AST_VECTOR_SIZE(&codecs->payload_mapping_rx)) {
@ -1105,9 +1109,27 @@ static void rtp_codecs_payload_replace_rx(struct ast_rtp_codecs *codecs, int pay
return;
}
payload_mapping_rx_clear_primary(codecs, new_type);
if (replace) {
payload_mapping_rx_clear_primary(codecs, new_type);
}
}
/*!
* \internal
* \brief Put the new_type into the rx payload type mapping.
* \since 14.0.0
*
* \param codecs Codecs structure to put new_type into
* \param payload type position to replace.
* \param new_type RTP payload mapping object to store.
*
* \note It is assumed that codecs is write locked before calling.
*/
static void rtp_codecs_payload_replace_rx(struct ast_rtp_codecs *codecs, int payload, struct ast_rtp_payload_type *new_type) {
rtp_codecs_payload_set_rx(codecs, payload, new_type, 1);
}
/*!
* \internal
* \brief Copy the rx payload type mapping to the destination.
@ -1180,6 +1202,9 @@ static int payload_mapping_tx_is_present(const struct ast_rtp_codecs *codecs, co
} else if (!current->asterisk_format && !to_match->asterisk_format) {
if (current->rtp_code != to_match->rtp_code) {
continue;
} else if (to_match->rtp_code == AST_RTP_DTMF && current->sample_rate != to_match->sample_rate) {
/* it is possible for multiple DTMF types to exist with different sample rates */
continue;
}
} else {
continue;
@ -1262,6 +1287,7 @@ void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_cod
rtp_codecs_payloads_copy_rx(src, dest, instance);
rtp_codecs_payloads_copy_tx(src, dest, instance);
dest->framing = src->framing;
ao2_replace(dest->preferred_format, src->preferred_format);
ast_rwlock_unlock(&src->codecs_lock);
ast_rwlock_unlock(&dest->codecs_lock);
@ -1304,6 +1330,7 @@ void ast_rtp_codecs_payloads_xover(struct ast_rtp_codecs *src, struct ast_rtp_co
}
dest->framing = src->framing;
ao2_replace(dest->preferred_format, src->preferred_format);
if (src != dest) {
ast_rwlock_unlock(&src->codecs_lock);
@ -1384,7 +1411,7 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs,
* then this not a match; if one has not been supplied, then the
* rates are not compared */
if (sample_rate && t->sample_rate &&
(sample_rate != t->sample_rate)) {
(sample_rate != t->sample_rate)) {
continue;
}
@ -1399,6 +1426,7 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs,
new_type->rtp_code = t->payload_type.rtp_code;
new_type->payload = pt;
new_type->primary_mapping = 1;
new_type->sample_rate = sample_rate;
if (t->payload_type.asterisk_format
&& ast_format_cmp(t->payload_type.format, ast_format_g726) == AST_FORMAT_CMP_EQUAL
&& (options & AST_RTP_OPT_G726_NONSTANDARD)) {
@ -1457,6 +1485,10 @@ void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp
if (payload < AST_VECTOR_SIZE(&codecs->payload_mapping_tx)) {
type = AST_VECTOR_GET(&codecs->payload_mapping_tx, payload);
/* remove the preferred format if we are unsetting its container. */
if (ast_format_cmp(type->format, codecs->preferred_format) == AST_FORMAT_CMP_EQUAL) {
ao2_replace(codecs->preferred_format, NULL);
}
ao2_cleanup(type);
AST_VECTOR_REPLACE(&codecs->payload_mapping_tx, payload, NULL);
}
@ -1513,6 +1545,23 @@ struct ast_rtp_payload_type *ast_rtp_codecs_get_payload(struct ast_rtp_codecs *c
return type;
}
struct ast_format *ast_rtp_codecs_get_preferred_format(struct ast_rtp_codecs *codecs)
{
struct ast_format *format;
ast_rwlock_rdlock(&codecs->codecs_lock);
format = ao2_bump(codecs->preferred_format);
ast_rwlock_unlock(&codecs->codecs_lock);
return format;
}
int ast_rtp_codecs_set_preferred_format(struct ast_rtp_codecs *codecs, struct ast_format *format)
{
ast_rwlock_wrlock(&codecs->codecs_lock);
ao2_replace(codecs->preferred_format, format);
ast_rwlock_unlock(&codecs->codecs_lock);
return 0;
}
int ast_rtp_codecs_payload_replace_format(struct ast_rtp_codecs *codecs, int payload, struct ast_format *format)
{
struct ast_rtp_payload_type *type;
@ -1825,10 +1874,11 @@ static int rtp_codecs_find_non_primary_dynamic_rx(struct ast_rtp_codecs *codecs)
* \return Numerical payload type
* \retval -1 if could not assign.
*/
static int rtp_codecs_assign_payload_code_rx(struct ast_rtp_codecs *codecs, int asterisk_format, struct ast_format *format, int code, int explicit)
static int rtp_codecs_assign_payload_code_rx(struct ast_rtp_codecs *codecs, int asterisk_format, struct ast_format *format, int code, int explicit, unsigned int sample_rate)
{
int payload = code;
int payload = code, i;
struct ast_rtp_payload_type *new_type;
static struct ast_rtp_payload_type *ignore[AST_RTP_MAX_PT] = {0};
if (!explicit) {
payload = find_static_payload_type(asterisk_format, format, code);
@ -1838,22 +1888,54 @@ static int rtp_codecs_assign_payload_code_rx(struct ast_rtp_codecs *codecs, int
}
}
new_type = rtp_payload_type_alloc(format, payload, code, 1);
new_type = rtp_payload_type_alloc(format, payload, code, 1, sample_rate);
if (!new_type) {
return -1;
}
ast_rwlock_wrlock(&codecs->codecs_lock);
/* Go through the existing mapping to create an ignore list. */
for (i = 0; i < AST_VECTOR_SIZE(&codecs->payload_mapping_rx); i++) {
if (AST_VECTOR_GET(&codecs->payload_mapping_rx, i)) {
ignore[i] = static_RTP_PT[i];
}
}
if (payload > -1 && (payload < AST_RTP_PT_FIRST_DYNAMIC
|| AST_VECTOR_SIZE(&codecs->payload_mapping_rx) <= payload
|| !AST_VECTOR_GET(&codecs->payload_mapping_rx, payload))) {
/*
* The payload type is a static assignment
* or our default dynamic position is available.
*/
rtp_codecs_payload_replace_rx(codecs, payload, new_type);
} else if (payload > -1 && !explicit
/* We can either call this with the full list or the current rx list. The former
* (static_RTP_PT) results in payload types skipping statically 'used' slots so you
* get 101, 113...
* With the latter (the built ingore list) you get what's expected 101, 102, 103 under
* most circumstances, but this results in static types being replaced. Probably fine
* because we preclude the current list.
*/
&& (-1 < (payload = find_unused_payload_in_range(codecs, payload, AST_RTP_MAX_PT, ignore)))) {
/*
* Our dynamic position is currently in use.
* Try for the numerically next free one before trying
* across the full range. This keeps the payload id's
* in the best numerical order we can through the free
* types.
*/
new_type->payload = payload;
/*
* In this case, consider this the primary mapping for
* the payload type so don't clear it. Set not replace.
*/
rtp_codecs_payload_set_rx(codecs, payload, new_type, 0);
} else if (!explicit && (-1 < (payload = find_unused_payload(codecs))
|| -1 < (payload = rtp_codecs_find_non_primary_dynamic_rx(codecs)))) {
|| -1 < (payload = rtp_codecs_find_non_primary_dynamic_rx(codecs)))) {
/*
* We found the first available empty dynamic position
* or we found a mapping that should no longer be
@ -1884,6 +1966,11 @@ static int rtp_codecs_assign_payload_code_rx(struct ast_rtp_codecs *codecs, int
}
int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_format, struct ast_format *format, int code)
{
return ast_rtp_codecs_payload_code_sample_rate(codecs, asterisk_format, format, code, 0);
}
int ast_rtp_codecs_payload_code_sample_rate(struct ast_rtp_codecs *codecs, int asterisk_format, struct ast_format *format, int code, unsigned int sample_rate)
{
struct ast_rtp_payload_type *type;
int idx;
@ -1900,7 +1987,8 @@ int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_form
if (!type->asterisk_format
&& type->primary_mapping
&& type->rtp_code == code) {
&& type->rtp_code == code
&& (sample_rate == 0 || type->sample_rate == sample_rate)) {
payload = idx;
break;
}
@ -1926,7 +2014,7 @@ int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_form
if (payload < 0) {
payload = rtp_codecs_assign_payload_code_rx(codecs, asterisk_format, format,
code, 0);
code, 0, sample_rate);
}
ast_rwlock_unlock(&static_RTP_PT_lock);
@ -1935,10 +2023,15 @@ int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_form
int ast_rtp_codecs_payload_set_rx(struct ast_rtp_codecs *codecs, int code, struct ast_format *format)
{
return rtp_codecs_assign_payload_code_rx(codecs, 1, format, code, 1);
return rtp_codecs_assign_payload_code_rx(codecs, 1, format, code, 1, 0);
}
int ast_rtp_codecs_payload_code_tx(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code)
int ast_rtp_codecs_payload_set_rx_sample_rate(struct ast_rtp_codecs *codecs, int code, struct ast_format *format, unsigned int sample_rate)
{
return rtp_codecs_assign_payload_code_rx(codecs, 1, format, code, 0, sample_rate);
}
int ast_rtp_codecs_payload_code_tx_sample_rate(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code, unsigned int sample_rate)
{
struct ast_rtp_payload_type *type;
int idx;
@ -1953,7 +2046,11 @@ int ast_rtp_codecs_payload_code_tx(struct ast_rtp_codecs *codecs, int asterisk_f
}
if (!type->asterisk_format
&& type->rtp_code == code) {
&& type->rtp_code == code
/* Multiple DTMF types share an rtp code but have different sample rates. To ensure we have the right
type we therefore need the sample rate as well as the format and code. Other types have a fixed
sample rate so this is not needed. For those pass in a sample rate of 0 or use ast_rtp_codecs_payload_code_tx. */
&& (sample_rate == 0 || type->sample_rate == sample_rate)) {
payload = idx;
break;
}
@ -1985,6 +2082,11 @@ int ast_rtp_codecs_payload_code_tx(struct ast_rtp_codecs *codecs, int asterisk_f
return payload;
}
int ast_rtp_codecs_payload_code_tx(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code)
{
return ast_rtp_codecs_payload_code_tx_sample_rate(codecs, asterisk_format, format, code, 0);
}
int ast_rtp_codecs_find_payload_code(struct ast_rtp_codecs *codecs, int payload)
{
struct ast_rtp_payload_type *type;
@ -3255,7 +3357,7 @@ static void add_static_payload(int payload, struct ast_format *format, int rtp_c
}
}
type = rtp_payload_type_alloc(format, payload, rtp_code, 1);
type = rtp_payload_type_alloc(format, payload, rtp_code, 1, 0);
if (type) {
ao2_cleanup(static_RTP_PT[payload]);
static_RTP_PT[payload] = type;
@ -3717,7 +3819,12 @@ int ast_rtp_engine_init(void)
/* this is the sample rate listed in the RTP profile for the G.722 codec, *NOT* the actual sample rate of the media stream */
set_next_mime_type(ast_format_g722, 0, "audio", "G722", 8000);
set_next_mime_type(ast_format_g726_aal2, 0, "audio", "AAL2-G726-32", 8000);
/* we need all possible dtmf/bitrate combinations or ast_rtp_codecs_payloads_set_rtpmap_type_rate will not examine it */
set_next_mime_type(NULL, AST_RTP_DTMF, "audio", "telephone-event", 8000);
set_next_mime_type(NULL, AST_RTP_DTMF, "audio", "telephone-event", 16000);
set_next_mime_type(NULL, AST_RTP_DTMF, "audio", "telephone-event", 24000);
set_next_mime_type(NULL, AST_RTP_DTMF, "audio", "telephone-event", 32000);
set_next_mime_type(NULL, AST_RTP_DTMF, "audio", "telephone-event", 48000);
set_next_mime_type(NULL, AST_RTP_CISCO_DTMF, "audio", "cisco-telephone-event", 8000);
set_next_mime_type(NULL, AST_RTP_CN, "audio", "CN", 8000);
set_next_mime_type(ast_format_jpeg, 0, "video", "JPEG", 90000);

@ -538,6 +538,8 @@ static int set_caps(struct ast_sip_session *session,
ast_codec_media_type2str(session_media->type),
ast_format_cap_get_names(caps, &usbuf),
ast_format_cap_get_names(peer, &thembuf));
} else {
ast_rtp_codecs_set_preferred_format(&codecs, ast_format_cap_get_format(joint, 0));
}
if (is_offer) {
@ -559,7 +561,7 @@ static int set_caps(struct ast_sip_session *session,
AST_MEDIA_TYPE_UNKNOWN);
ast_format_cap_remove_by_type(caps, media_type);
if (session->endpoint->preferred_codec_only){
if (session->endpoint->preferred_codec_only) {
struct ast_format *preferred_fmt = ast_format_cap_get_format(joint, 0);
ast_format_cap_append(caps, preferred_fmt, 0);
ao2_ref(preferred_fmt, -1);
@ -650,6 +652,42 @@ static pjmedia_sdp_attr* generate_rtpmap_attr(struct ast_sip_session *session, p
return attr;
}
static pjmedia_sdp_attr* generate_rtpmap_attr2(struct ast_sip_session *session, pjmedia_sdp_media *media, pj_pool_t *pool,
int rtp_code, int asterisk_format, struct ast_format *format, int code, int sample_rate)
{
#ifndef HAVE_PJSIP_ENDPOINT_COMPACT_FORM
extern pj_bool_t pjsip_use_compact_form;
#else
pj_bool_t pjsip_use_compact_form = pjsip_cfg()->endpt.use_compact_form;
#endif
pjmedia_sdp_rtpmap rtpmap;
pjmedia_sdp_attr *attr = NULL;
char tmp[64];
enum ast_rtp_options options = session->endpoint->media.g726_non_standard ?
AST_RTP_OPT_G726_NONSTANDARD : 0;
snprintf(tmp, sizeof(tmp), "%d", rtp_code);
pj_strdup2(pool, &media->desc.fmt[media->desc.fmt_count++], tmp);
if (rtp_code <= AST_RTP_PT_LAST_STATIC && pjsip_use_compact_form) {
return NULL;
}
rtpmap.pt = media->desc.fmt[media->desc.fmt_count - 1];
rtpmap.clock_rate = sample_rate;
pj_strdup2(pool, &rtpmap.enc_name, ast_rtp_lookup_mime_subtype2(asterisk_format, format, code, options));
if (!pj_stricmp2(&rtpmap.enc_name, "opus")) {
pj_cstr(&rtpmap.param, "2");
} else {
pj_cstr(&rtpmap.param, NULL);
}
pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
return attr;
}
static pjmedia_sdp_attr* generate_fmtp_attr(pj_pool_t *pool, struct ast_format *format, int rtp_code)
{
struct ast_str *fmtp0 = ast_str_alloca(256);
@ -1749,6 +1787,13 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
pj_sockaddr ip;
int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) &&
ast_format_cap_count(session->direct_media_cap);
/* Keep track of the sample rates for offered codecs so we can build matching
RFC 2833/4733 payload offers. */
AST_VECTOR(, int) sample_rates;
/* In case we can't init the sample rates, still try to do the rest. */
int build_dtmf_sample_rates = 1;
SCOPE_ENTER(1, "%s Type: %s %s\n", ast_sip_session_get_name(session),
ast_codec_media_type2str(media_type), ast_str_tmp(128, ast_stream_to_str(stream, &STR_TMP)));
@ -1900,6 +1945,12 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
ast_format_cap_append_from_cap(caps, ast_stream_get_formats(stream), media_type);
}
/* Init the sample rates before we start adding them. Assume we will have at least one. */
if (AST_VECTOR_INIT(&sample_rates, 1)) {
ast_log(LOG_ERROR, "Unable to add dtmf formats to SDP!\n");
build_dtmf_sample_rates = 0;
}
for (index = 0; index < ast_format_cap_count(caps); ++index) {
struct ast_format *format = ast_format_cap_get_format(caps, index);
@ -1938,7 +1989,24 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
}
if ((attr = generate_rtpmap_attr(session, media, pool, rtp_code, 1, format, 0))) {
int newrate = ast_rtp_lookup_sample_rate2(1, format, 0);
int i, added = 0;
media->attr[media->attr_count++] = attr;
if (build_dtmf_sample_rates) {
for (i = 0; i < AST_VECTOR_SIZE(&sample_rates); i++) {
/* Only add if we haven't already processed this sample rate. For instance
A-law and u-law 'share' one 8K DTMF payload type. */
if (newrate == AST_VECTOR_GET(&sample_rates, i)) {
added = 1;
break;
}
}
if (!added) {
AST_VECTOR_APPEND(&sample_rates, newrate);
}
}
}
if ((attr = generate_fmtp_attr(pool, format, rtp_code))) {
@ -1963,20 +2031,38 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
if (!(noncodec & index)) {
continue;
}
rtp_code = ast_rtp_codecs_payload_code(
ast_rtp_instance_get_codecs(session_media->rtp), 0, NULL, index);
if (rtp_code == -1) {
continue;
}
if ((attr = generate_rtpmap_attr(session, media, pool, rtp_code, 0, NULL, index))) {
media->attr[media->attr_count++] = attr;
}
if (index == AST_RTP_DTMF) {
snprintf(tmp, sizeof(tmp), "%d 0-16", rtp_code);
attr = pjmedia_sdp_attr_create(pool, "fmtp", pj_cstr(&stmp, tmp));
media->attr[media->attr_count++] = attr;
if (index != AST_RTP_DTMF) {
rtp_code = ast_rtp_codecs_payload_code(
ast_rtp_instance_get_codecs(session_media->rtp), 0, NULL, index);
if (rtp_code == -1) {
continue;
} else if ((attr = generate_rtpmap_attr(session, media, pool, rtp_code, 0, NULL, index))) {
media->attr[media->attr_count++] = attr;
}
} else if (build_dtmf_sample_rates) {
/*
* Walk through the possible bitrates for the RFC 2833/4733 digits and generate the rtpmap
* attributes.
*/
int i;
for (i = 0; i < AST_VECTOR_SIZE(&sample_rates); i++) {
rtp_code = ast_rtp_codecs_payload_code_sample_rate(
ast_rtp_instance_get_codecs(session_media->rtp), 0, NULL, index, AST_VECTOR_GET(&sample_rates, i));
if (rtp_code == -1) {
continue;
}
if ((attr = generate_rtpmap_attr2(session, media, pool, rtp_code, 0, NULL, index, AST_VECTOR_GET(&sample_rates, i)))) {
media->attr[media->attr_count++] = attr;
snprintf(tmp, sizeof(tmp), "%d 0-16", (rtp_code));
attr = pjmedia_sdp_attr_create(pool, "fmtp", pj_cstr(&stmp, tmp));
media->attr[media->attr_count++] = attr;
}
}
}
if (media->desc.fmt_count == PJMEDIA_MAX_SDP_FMT) {
@ -1985,6 +2071,8 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
}
}
/* we are done with the sample rates */
AST_VECTOR_FREE(&sample_rates);
/* If no formats were actually added to the media stream don't add it to the SDP */
if (!media->desc.fmt_count) {

@ -138,7 +138,6 @@
#define RTCP_PT_PSFB AST_RTP_RTCP_PSFB
#define RTP_MTU 1200
#define DTMF_SAMPLE_RATE_MS 8 /*!< DTMF samples per millisecond */
#define DEFAULT_DTMF_TIMEOUT (150 * (8000 / 1000)) /*!< samples */
@ -434,6 +433,7 @@ struct ast_rtp {
unsigned int dtmf_timeout; /*!< When this timestamp is reached we consider END frame lost and forcibly abort digit */
unsigned int dtmfsamples;
enum ast_rtp_dtmf_mode dtmfmode; /*!< The current DTMF mode of the RTP stream */
unsigned int dtmf_samplerate_ms; /*!< The sample rate of the current RTP stream in ms (sample rate / 1000) */
/* DTMF Transmission Variables */
unsigned int lastdigitts;
char sending_digit; /*!< boolean - are we sending digits */
@ -4284,8 +4284,10 @@ static int ast_rtp_dtmf_begin(struct ast_rtp_instance *instance, char digit)
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
struct ast_sockaddr remote_address = { {0,} };
int hdrlen = 12, res = 0, i = 0, payload = 101;
unsigned int sample_rate = 8000;
char data[256];
unsigned int *rtpheader = (unsigned int*)data;
RAII_VAR(struct ast_format *, payload_format, NULL, ao2_cleanup);
ast_rtp_instance_get_remote_address(instance, &remote_address);
@ -4310,12 +4312,32 @@ static int ast_rtp_dtmf_begin(struct ast_rtp_instance *instance, char digit)
return -1;
}
/* Grab the payload that they expect the RFC2833 packet to be received in */
payload = ast_rtp_codecs_payload_code_tx(ast_rtp_instance_get_codecs(instance), 0, NULL, AST_RTP_DTMF);
if (rtp->lasttxformat == ast_format_none) {
/* No audio frames have been written yet so we have to lookup both the preferred payload type and bitrate. */
payload_format = ast_rtp_codecs_get_preferred_format(ast_rtp_instance_get_codecs(instance));
if (payload_format) {
/* If we have a preferred type, use that. Otherwise default to 8K. */
sample_rate = ast_format_get_sample_rate(payload_format);
}
} else {
sample_rate = ast_format_get_sample_rate(rtp->lasttxformat);
}
/* Grab the matching DTMF type payload */
payload = ast_rtp_codecs_payload_code_tx_sample_rate(ast_rtp_instance_get_codecs(instance), 0, NULL, AST_RTP_DTMF, sample_rate);
/* If this returns -1, we are being asked to send digits for a sample rate that is outside
what was negotiated for. Fall back if possible. */
if (payload == -1) {
return -1;
}
ast_test_suite_event_notify("DTMF_BEGIN", "Digit: %d\r\nPayload: %d\r\nRate: %d\r\n", digit, payload, sample_rate);
ast_debug(1, "Sending digit '%d' at rate %d with payload %d\n", digit, sample_rate, payload);
rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000));
rtp->send_duration = 160;
rtp->lastts += calc_txstamp(rtp, NULL) * DTMF_SAMPLE_RATE_MS;
rtp->dtmf_samplerate_ms = (sample_rate / 1000);
rtp->lastts += calc_txstamp(rtp, NULL) * rtp->dtmf_samplerate_ms;
rtp->lastdigitts = rtp->lastts + rtp->send_duration;
/* Create the actual packet that we will be sending */
@ -4394,7 +4416,7 @@ static int ast_rtp_dtmf_continuation(struct ast_rtp_instance *instance)
/* And now we increment some values for the next time we swing by */
rtp->seqno++;
rtp->send_duration += 160;
rtp->lastts += calc_txstamp(rtp, NULL) * DTMF_SAMPLE_RATE_MS;
rtp->lastts += calc_txstamp(rtp, NULL) * rtp->dtmf_samplerate_ms;
return 0;
}
@ -4472,7 +4494,7 @@ static int ast_rtp_dtmf_end_with_duration(struct ast_rtp_instance *instance, cha
res = 0;
/* Oh and we can't forget to turn off the stuff that says we are sending DTMF */
rtp->lastts += calc_txstamp(rtp, NULL) * DTMF_SAMPLE_RATE_MS;
rtp->lastts += calc_txstamp(rtp, NULL) * rtp->dtmf_samplerate_ms;
/* Reset the smoother as the delivery time stored in it is now out of date */
if (rtp->smoother) {

Loading…
Cancel
Save