From df8b425ae6ea83c5c6e67028dfca1c60e1b51354 Mon Sep 17 00:00:00 2001 From: Mike Bradeen Date: Tue, 28 Nov 2023 09:42:20 -0700 Subject: [PATCH] rtp_engine: add support for multirate 2833 DRAFT DRAFT PR - demonstrate a method for handling non 8K 2833 digit sdp offers. Changes to the engine itself limited to adding the 48/24K types. res_pjsip_sdp_rtp is changed to note the number of incoming codecs and add an associated offer --- include/asterisk/rtp_engine.h | 32 +++++++++ main/rtp_engine.c | 33 ++++++++-- res/res_pjsip_sdp_rtp.c | 119 +++++++++++++++++++++++++++++++--- res/res_rtp_asterisk.c | 37 +++++++++-- 4 files changed, 204 insertions(+), 17 deletions(-) diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h index 7834acd033b..7e674481f1f 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; + /*! Bitrate to over-ride mime type defaults */ + int bitrate; }; /* Common RTCP report types */ @@ -1788,6 +1790,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 bitrate for use with a specific Asterisk format + * + * \param codecs Codecs structure to manipulate + * \param code The payload code + * \param format Asterisk format + * \param bitrate Bitrate 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 20.0.0 + */ +int ast_rtp_codecs_payload_set_rx_bitrate(struct ast_rtp_codecs *codecs, int code, struct ast_format *format, int bitrate); + /*! * \brief Retrieve a tx mapped payload type based on whether it is an Asterisk format and the code * \since 14.0.0 @@ -1802,6 +1819,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 20.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 bitrate The bitrate 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_bitrate(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code, int bitrate); + /*! * \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 1b8e9c51ea0..98046d721c4 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -1380,6 +1380,8 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, continue; } + ast_log(LOG_DEBUG, "Comparing %d %s %s with %d %s %s", pt, mimetype, mimesubtype, t->payload_type.rtp_code, t->type, t->subtype); + /* if both sample rates have been supplied, and they don't match, * then this not a match; if one has not been supplied, then the * rates are not compared */ @@ -1399,6 +1401,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->bitrate = 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)) { @@ -1825,7 +1828,7 @@ 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, int bitrate) { int payload = code; struct ast_rtp_payload_type *new_type; @@ -1844,8 +1847,13 @@ static int rtp_codecs_assign_payload_code_rx(struct ast_rtp_codecs *codecs, int } ast_rwlock_wrlock(&codecs->codecs_lock); + + new_type->bitrate = bitrate; + if (payload > -1 && (payload < AST_RTP_PT_FIRST_DYNAMIC || AST_VECTOR_SIZE(&codecs->payload_mapping_rx) <= payload + // test if this is needed + //|| (explicit || !AST_VECTOR_GET(&codecs->payload_mapping_rx, payload)))) { || !AST_VECTOR_GET(&codecs->payload_mapping_rx, payload))) { /* * The payload type is a static assignment @@ -1926,7 +1934,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, 0); } ast_rwlock_unlock(&static_RTP_PT_lock); @@ -1935,10 +1943,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_bitrate(struct ast_rtp_codecs *codecs, int code, struct ast_format *format, int bitrate) +{ + return rtp_codecs_assign_payload_code_rx(codecs, 1, format, code, 0, bitrate); +} + +int ast_rtp_codecs_payload_code_tx_bitrate(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code, int bitrate) { struct ast_rtp_payload_type *type; int idx; @@ -1953,7 +1966,8 @@ 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 + && (!bitrate || type->bitrate == bitrate)) { payload = idx; break; } @@ -1985,6 +1999,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_bitrate(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; @@ -3700,7 +3719,11 @@ 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 4 of these 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", 48000); + set_next_mime_type(NULL, AST_RTP_DTMF, "audio", "telephone-event", 24000); 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 8d1c5c36359..345987edc8e 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -650,6 +650,43 @@ 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; + ast_log(LOG_ERROR, "Generating rtpmap attribute with clock rate: %d\n", rtpmap.clock_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 +1786,11 @@ 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); + + int bitRates[4] = { 0, 0, 0, 0 }; + int added_rtp_type = 0; + struct ast_rtp_codecs *zcodecs; + 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))); @@ -1938,7 +1980,27 @@ 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); media->attr[media->attr_count++] = attr; + // lolcode + if (bitRates[0] == 0) { + bitRates[0] = newrate; + } + else if ((newrate != bitRates[0])) { + if (bitRates[1] == 0) { + bitRates[1] = newrate; + } + else if ((newrate != bitRates[1])) { + if (bitRates[2] == 0) { + bitRates[2] = newrate; + } + else if ((newrate != bitRates[2])) { + if (bitRates[3] == 0) { + bitRates[3] = newrate; + } + } + } + } } if ((attr = generate_fmtp_attr(pool, format, rtp_code))) { @@ -1956,6 +2018,38 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as } } + /* Copy any non asterisk formats to the rx side */ + zcodecs = ast_rtp_instance_get_codecs(session_media->rtp); + for (int idx = 0; idx < AST_VECTOR_SIZE(&zcodecs->payload_mapping_tx); ++idx) { + struct ast_rtp_payload_type *type; + type = AST_VECTOR_GET(&zcodecs->payload_mapping_tx, idx); + if (!type) { + continue; + } + if (!type->asterisk_format && type->primary_mapping) { + ast_rtp_codecs_payload_set_rx_bitrate(ast_rtp_instance_get_codecs(session_media->rtp), idx, type->format, type->bitrate); + } + } + /* Use the rx list of non asterisk formats to build the sdp attributes */ + for (int idx = 0; idx < AST_VECTOR_SIZE(&zcodecs->payload_mapping_rx); ++idx) { + struct ast_rtp_payload_type *type; + type = AST_VECTOR_GET(&zcodecs->payload_mapping_rx, idx); + if (!type) { + continue; + } + if (!type->asterisk_format && type->primary_mapping) { + int newrate = type->bitrate; + if ((attr = generate_rtpmap_attr2(session, media, pool, idx, 0, NULL, AST_RTP_DTMF, newrate))) { + /* note that we are adding attributes based on an existing offer so they don't need to be made from scratch */ + added_rtp_type = 1; + media->attr[media->attr_count++] = attr; + snprintf(tmp, sizeof(tmp), "%d 0-16", (idx)); + attr = pjmedia_sdp_attr_create(pool, "fmtp", pj_cstr(&stmp, tmp)); + media->attr[media->attr_count++] = attr; + } + } + } + /* Add non-codec formats */ if (ast_sip_session_is_pending_stream_default(session, stream) && media_type != AST_MEDIA_TYPE_VIDEO && media->desc.fmt_count < PJMEDIA_MAX_SDP_FMT) { @@ -1969,14 +2063,24 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as 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) { + 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; + else if (!added_rtp_type) { + /* If we aren't going based off an existing offer, walk through the possible bitrates for the + 2833/4733 digits and add them manually */ + for (int i=0; i<4; i++) { + if(bitRates[i] != 0 ) { + if ((attr = generate_rtpmap_attr2(session, media, pool, (rtp_code+i), 0, NULL, index, bitRates[i]))) { + media->attr[media->attr_count++] = attr; + } + snprintf(tmp, sizeof(tmp), "%d 0-16", (rtp_code+i)); + 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,7 +2089,6 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as } } - /* If no formats were actually added to the media stream don't add it to the SDP */ if (!media->desc.fmt_count) { SCOPE_EXIT_RTN_VALUE(1, "No formats added to stream\n"); diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 7a79de2c8d7..967eafab572 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -4204,7 +4204,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; + int hdrlen = 12, res = 0, i = 0, payload = 101, sample_rate = 8000; char data[256]; unsigned int *rtpheader = (unsigned int*)data; @@ -4231,12 +4231,41 @@ 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) { + /* we haven't sent any audio yet so we have to lookup both the payload type and + bitrate. This should get us the first stored dtmf type. */ + struct ast_rtp_payload_type *type; + int idx; + struct ast_rtp_codecs *codecs = ast_rtp_instance_get_codecs(instance); + ast_rwlock_rdlock(&codecs->codecs_lock); + for (idx = 0; idx < AST_VECTOR_SIZE(&codecs->payload_mapping_tx); ++idx) { + type = AST_VECTOR_GET(&codecs->payload_mapping_tx, idx); + if (!type) { + continue; + } + if (type->rtp_code == AST_RTP_DTMF) { + payload = idx; + sample_rate = type->bitrate; + break; + } + } + ast_rwlock_unlock(&codecs->codecs_lock); + } else { + /* If we get 0 back that can be ok, provided a default rate is set */ + sample_rate = ast_format_get_codec(rtp->lasttxformat)->sample_rate; + /* Grab the payload that they expect the RFC2833 packet to be received in */ + payload = ast_rtp_codecs_payload_code_tx_bitrate(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_log(LOG_DEBUG, "Sending digit '%d' at rate %d\n", digit, sample_rate); 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->lastts += calc_txstamp(rtp, NULL) * (sample_rate/1000); rtp->lastdigitts = rtp->lastts + rtp->send_duration; /* Create the actual packet that we will be sending */