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 624f509ce4)
This commit is contained in:
Mike Bradeen
2024-04-08 11:00:14 -06:00
committed by Asterisk Development Team
parent 3b1125633e
commit cf564713a4
4 changed files with 361 additions and 36 deletions

View File

@@ -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) {