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 */
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 {
*/
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
*
*/
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
*
*/
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
*/
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
*
}
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);
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)
}
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);
}
/*!
* \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)) {
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.
} 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;
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);
}
dest->framing = src->framing;
+ ao2_replace(dest->preferred_format, src->preferred_format);
if (src != dest) {
ast_rwlock_unlock(&src->codecs_lock);
* 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;
}
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)) {
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);
}
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;
* \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);
}
}
- 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
}
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;
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;
}
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);
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;
}
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;
}
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;
}
}
- 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;
/* 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);
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) {
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);
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);
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)));
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);
}
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))) {
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) {
}
}
+ /* 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) {
#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 */
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 */
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);
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 */
/* 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;
}
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) {