mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-29 18:19:30 +00:00
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
This commit is contained in:
@@ -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) {
|
||||
|
Reference in New Issue
Block a user