diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h index 7834acd033..cb555b7797 100644 --- a/include/asterisk/rtp_engine.h +++ b/include/asterisk/rtp_engine.h @@ -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 * diff --git a/main/rtp_engine.c b/main/rtp_engine.c index ceaac12e82..604aab627e 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -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); diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index 60023863b5..5e992350ae 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -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) { diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index a309c3317d..db763e3ed0 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -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) {