diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h index b3519f9176..0a38e43cd5 100644 --- a/include/asterisk/rtp_engine.h +++ b/include/asterisk/rtp_engine.h @@ -764,10 +764,15 @@ struct ast_rtp_codecs { unsigned int framing; /*! The preferred format, as the mappings are numerically sorted */ struct ast_format *preferred_format; + /*! The preferred dtmf sample rate */ + int preferred_dtmf_rate; + /*! The preferred dtmf payload type */ + int preferred_dtmf_pt; }; #define AST_RTP_CODECS_NULL_INIT \ - { .codecs_lock = AST_RWLOCK_INIT_VALUE, .payload_mapping_rx = { 0, }, .payload_mapping_tx = { 0, }, .framing = 0, .preferred_format = NULL } + { .codecs_lock = AST_RWLOCK_INIT_VALUE, .payload_mapping_rx = { 0, }, .payload_mapping_tx = { 0, }, .framing = 0, .preferred_format = NULL, \ + .preferred_dtmf_rate = -1, .preferred_dtmf_pt = -1} /*! Structure that represents the glue that binds an RTP instance to a channel */ struct ast_rtp_glue { @@ -1710,6 +1715,69 @@ struct ast_format *ast_rtp_codecs_get_preferred_format(struct ast_rtp_codecs *co */ int ast_rtp_codecs_set_preferred_format(struct ast_rtp_codecs *codecs, struct ast_format *format); +/*! + * \brief Retrieve rx preferred dtmf format payload type + * + * \param codecs Codecs structure to look in + * + * \return Payload type of preferred dtmf format. + * \retval -1 if not set. + * + * Example usage: + * + * \code + * int payload; + * payload = ast_rtp_codecs_get_preferred_dtmf_format_pt(codec); + * \endcode + * + * This looks up the preferred dtmf format pt on the codec + */ +int ast_rtp_codecs_get_preferred_dtmf_format_pt(struct ast_rtp_codecs *codecs); + +/*! + * \brief Retrieve rx preferred dtmf format sample rate + * + * \param codecs Codecs structure to look in + * + * \return Sample rate of preferred dtmf format. + * \retval -1 if not set. + * + * Example usage: + * + * \code + * int sample_rate; + * sample_rate = ast_rtp_codecs_get_preferred_dtmf_format_rate(codec); + * \endcode + * + * This looks up the preferred dtmf format sample rate on the codec + */ +int ast_rtp_codecs_get_preferred_dtmf_format_rate(struct ast_rtp_codecs *codecs); + +/*! + * \brief Set the preferred dtmf format pt and sample rate + * + * \param codecs Codecs structure to set the preferred format in + * \param pt Preferred dtmf payload type to set. + * \param rate Preferred dtmf payload rate 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 + * int dtmf_code = atoi(dtmf_pt); + * int dtmf_rate = clock_rate; + * ast_rtp_codecs_set_preferred_dtmf_format(codecs, dtmf_code, dtmf_rate); + * \endcode + * + * This sets the preferred dtmf_code and dtmf_rate on the codec. + */ +int ast_rtp_codecs_set_preferred_dtmf_format(struct ast_rtp_codecs *codecs, int pt, int rate); + + /*! * \brief Update the format associated with a tx payload type in a codecs structure * @@ -2977,6 +3045,20 @@ int ast_rtp_get_rate(const struct ast_format *format); */ struct stasis_topic *ast_rtp_topic(void); +/*! + * \brief Determine if a type of payload is already present in mappings. + * \since 18 + * + * \param codecs Codecs to be checked for mappings. + * \param to_match Payload type object to compare against. + * + * \note It is assumed that codecs is not locked before calling. + * + * \retval 0 not found + * \retval 1 found + */ +int ast_rtp_payload_mapping_tx_is_present(struct ast_rtp_codecs *codecs, const struct ast_rtp_payload_type *to_match); + /* RTP debug logging category name */ #define AST_LOG_CATEGORY_RTP "rtp" /* RTP packet debug logging category name */ diff --git a/main/rtp_engine.c b/main/rtp_engine.c index 621f12ca00..6b2d67bd31 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -1217,6 +1217,16 @@ static int payload_mapping_tx_is_present(const struct ast_rtp_codecs *codecs, co return 0; } +int ast_rtp_payload_mapping_tx_is_present(struct ast_rtp_codecs *codecs, const struct ast_rtp_payload_type *to_match) { + int ret = 0; + if (codecs && to_match) { + ast_rwlock_rdlock(&codecs->codecs_lock); + ret = payload_mapping_tx_is_present(codecs, to_match); + ast_rwlock_unlock(&codecs->codecs_lock); + } + return ret; +} + /*! * \internal * \brief Copy the tx payload type mapping to the destination. @@ -1289,6 +1299,8 @@ void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_cod rtp_codecs_payloads_copy_tx(src, dest, instance); dest->framing = src->framing; ao2_replace(dest->preferred_format, src->preferred_format); + dest->preferred_dtmf_rate = src->preferred_dtmf_rate; + dest->preferred_dtmf_pt = src->preferred_dtmf_pt; ast_rwlock_unlock(&src->codecs_lock); ast_rwlock_unlock(&dest->codecs_lock); @@ -1332,6 +1344,8 @@ 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); + dest->preferred_dtmf_rate = src->preferred_dtmf_rate; + dest->preferred_dtmf_pt = src->preferred_dtmf_pt; if (src != dest) { ast_rwlock_unlock(&src->codecs_lock); @@ -1572,6 +1586,33 @@ int ast_rtp_codecs_set_preferred_format(struct ast_rtp_codecs *codecs, struct as return 0; } +int ast_rtp_codecs_get_preferred_dtmf_format_pt(struct ast_rtp_codecs *codecs) +{ + int pt = -1; + ast_rwlock_rdlock(&codecs->codecs_lock); + pt = codecs->preferred_dtmf_pt; + ast_rwlock_unlock(&codecs->codecs_lock); + return pt; +} + +int ast_rtp_codecs_get_preferred_dtmf_format_rate(struct ast_rtp_codecs *codecs) +{ + int rate = -1; + ast_rwlock_rdlock(&codecs->codecs_lock); + rate = codecs->preferred_dtmf_rate; + ast_rwlock_unlock(&codecs->codecs_lock); + return rate; +} + +int ast_rtp_codecs_set_preferred_dtmf_format(struct ast_rtp_codecs *codecs, int pt, int rate) +{ + ast_rwlock_wrlock(&codecs->codecs_lock); + codecs->preferred_dtmf_pt = pt; + codecs->preferred_dtmf_rate = rate; + 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; @@ -2087,6 +2128,16 @@ int ast_rtp_codecs_payload_code_tx_sample_rate(struct ast_rtp_codecs *codecs, in ast_rwlock_rdlock(&static_RTP_PT_lock); payload = find_static_payload_type(asterisk_format, format, code); ast_rwlock_unlock(&static_RTP_PT_lock); + + ast_rwlock_rdlock(&codecs->codecs_lock); + if (payload >= 0 && payload < AST_VECTOR_SIZE(&codecs->payload_mapping_tx)){ + type = AST_VECTOR_GET(&codecs->payload_mapping_tx, payload); + if (!type || (sample_rate != 0 && type->sample_rate != sample_rate)) { + /* Don't use the type if we can't find it or it doesn't match the supplied sample_rate */ + payload = -1; + } + } + ast_rwlock_unlock(&codecs->codecs_lock); } return payload; diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index 98df545397..4423dfda5c 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -344,6 +344,14 @@ static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp ast_copy_pj_str(name, &rtpmap->enc_name, sizeof(name)); if (strcmp(name, "telephone-event") == 0) { + if (tel_event == 0) { + int dtmf_rate = 0, dtmf_code = 0; + char dtmf_pt[8]; + ast_copy_pj_str(dtmf_pt, &rtpmap->pt, sizeof(dtmf_pt)); + dtmf_code = atoi(dtmf_pt); + dtmf_rate = rtpmap->clock_rate; + ast_rtp_codecs_set_preferred_dtmf_format(codecs, dtmf_code, dtmf_rate); + } tel_event++; } diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index c3314612eb..495e3de93f 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -4283,8 +4283,7 @@ 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; + int hdrlen = 12, res = 0, i = 0, payload = -1, sample_rate = -1; char data[256]; unsigned int *rtpheader = (unsigned int*)data; RAII_VAR(struct ast_format *, payload_format, NULL, ao2_cleanup); @@ -4323,16 +4322,26 @@ static int ast_rtp_dtmf_begin(struct ast_rtp_instance *instance, char digit) 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 (sample_rate != -1) { + 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 using a codec with a sample rate that does not have a matching RFC 2833/4733 - offer. The offer may have included a default-rate one that doesn't match the codec rate, so try to use that. */ - if (payload == -1) { + if (payload == -1 || + !ast_rtp_payload_mapping_tx_is_present( + ast_rtp_instance_get_codecs(instance), ast_rtp_codecs_get_payload(ast_rtp_instance_get_codecs(instance), payload))) { + /* Fall back to the preferred DTMF payload type and sample rate as either we couldn't find an audio codec to try and match + sample rates with or we could, but a telephone-event matching that audio codec's sample rate was not included in the + sdp negotiated by the far end. */ + payload = ast_rtp_codecs_get_preferred_dtmf_format_pt(ast_rtp_instance_get_codecs(instance)); + sample_rate = ast_rtp_codecs_get_preferred_dtmf_format_rate(ast_rtp_instance_get_codecs(instance)); + } + + /* The sdp negotiation has not yeilded a usable RFC 2833/4733 format. Try a default-rate one as a last resort. */ + if (payload == -1 || sample_rate == -1) { sample_rate = DEFAULT_DTMF_SAMPLE_RATE_MS; payload = ast_rtp_codecs_payload_code_tx(ast_rtp_instance_get_codecs(instance), 0, NULL, AST_RTP_DTMF); } - /* No default-rate offer either, trying to send a digit outside of what was negotiated for. */ + /* Even trying a default payload has failed. We are trying to send a digit outside of what was negotiated for. */ if (payload == -1) { return -1; }