summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2015-04-10 23:37:20 +0000
committerRichard Mudgett <rmudgett@digium.com>2015-04-10 23:37:20 +0000
commitc499cabf530227fc5022ef411c32f3a95108ba2f (patch)
tree19814e3bfc3df3e29c988328e2ec9eae342ff29d
parent66f3fd0028959aea0bbd1a9caed41ad4bd7f20b8 (diff)
chan_pjsip/res_pjsip/bridge_softmix/core: Improve translation path choices.
With this patch, chan_pjsip/res_pjsip now sets the native formats to the codecs negotiated by a call. * The changes in chan_pjsip.c and res_pjsip_sdp_rtp.c set the native formats to include all the negotiated audio codecs instead of only the initial preferred audio codec and later the currently received audio codec. * The audio frame handling in channel.c:ast_read() is more streamlined and will automatically adjust to changes in received frame formats. The new policy is to remove translation and pass the new frame format to the receiver except if the translation was to a signed linear format. A more long winded version is commented in ast_read() along with some caveats. * The audio frame handling in channel.c:ast_write() is more streamlined and will automatically adjust any needed translation to changes in the frame formats sent. Frame formats sent can change for many reasons such as a recording is being played back or the bridged peer changed the format it sends. Since it is a normal expectation that sent formats can change, the codec mismatch warning message is demoted to a debug message. * Removed the short circuit check in channel.c:ast_channel_make_compatible_helper(). Two party bridges need to make channels compatible with each other. However, transfers and moving channels among bridges can result in otherwise compatible channels having sub-optimal translation paths if the make compatible check is short circuited. A result of forcing the reevaluation of channel compatibility is that the asterisk.conf:transcode_via_slin and codecs.conf:genericplc options take effect consistently now. It is unfortunate that these two options are enabled by default and negate some of the benefits to the changes in channel.c:ast_read() by forcing translation through signed linear on a two party bridge. * Improved the softmix bridge technology to better control the translation of frames to the bridge. All of the incoming translation is now normally handled by ast_read() instead of splitting any translation steps between ast_read() and the slin factory. If any frame comes in with an unexpected format then the translation path in ast_read() is updated for the next frame and the slin factory handles the current frame translation. This is the final patch in a series of patches aimed at improving translation path choices. The other patches are on the following reviews: https://reviewboard.asterisk.org/r/4600/ https://reviewboard.asterisk.org/r/4605/ ASTERISK-24841 #close Reported by: Matt Jordan Review: https://reviewboard.asterisk.org/r/4609/ ........ Merged revisions 434671 from http://svn.asterisk.org/svn/asterisk/branches/13 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@434672 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r--bridges/bridge_softmix.c73
-rw-r--r--channels/chan_pjsip.c16
-rw-r--r--include/asterisk/channel.h15
-rw-r--r--main/channel.c267
-rw-r--r--res/res_pjsip_sdp_rtp.c31
5 files changed, 258 insertions, 144 deletions
diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c
index eae28af14..b463f9936 100644
--- a/bridges/bridge_softmix.c
+++ b/bridges/bridge_softmix.c
@@ -57,6 +57,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define MAX_DATALEN 8096
+/*! The minimum sample rate of the bridge. */
+#define SOFTMIX_MIN_SAMPLE_RATE 8000 /* 8 kHz sample rate */
+
/*! \brief Interval at which mixing will take place. Valid options are 10, 20, and 40. */
#define DEFAULT_SOFTMIX_INTERVAL 20
@@ -96,8 +99,8 @@ struct softmix_channel {
struct ast_slinfactory factory;
/*! Frame that contains mixed audio to be written out to the channel */
struct ast_frame write_frame;
- /*! Frame that contains mixed audio read from the channel */
- struct ast_frame read_frame;
+ /*! Current expected read slinear format. */
+ struct ast_format *read_slin_format;
/*! DSP for detecting silence */
struct ast_dsp *dsp;
/*!
@@ -353,7 +356,9 @@ static void softmix_translate_helper_cleanup(struct softmix_translate_helper *tr
static void set_softmix_bridge_data(int rate, int interval, struct ast_bridge_channel *bridge_channel, int reset)
{
struct softmix_channel *sc = bridge_channel->tech_pvt;
- unsigned int channel_read_rate = ast_format_get_sample_rate(ast_channel_rawreadformat(bridge_channel->chan));
+ struct ast_format *slin_format;
+
+ slin_format = ast_format_cache_get_slin_by_rate(rate);
ast_mutex_lock(&sc->lock);
if (reset) {
@@ -369,31 +374,29 @@ static void set_softmix_bridge_data(int rate, int interval, struct ast_bridge_ch
* for the channel. The translated format may not be a
* static cached format.
*/
- ao2_replace(sc->write_frame.subclass.format, ast_format_cache_get_slin_by_rate(rate));
+ ao2_replace(sc->write_frame.subclass.format, slin_format);
sc->write_frame.data.ptr = sc->final_buf;
sc->write_frame.datalen = SOFTMIX_DATALEN(rate, interval);
sc->write_frame.samples = SOFTMIX_SAMPLES(rate, interval);
- /* Setup read frame parameters */
- sc->read_frame.frametype = AST_FRAME_VOICE;
/*
- * NOTE: The read_frame format does not hold a reference because it
+ * NOTE: The read_slin_format does not hold a reference because it
* will always be a signed linear format.
*/
- sc->read_frame.subclass.format = ast_format_cache_get_slin_by_rate(channel_read_rate);
- sc->read_frame.data.ptr = sc->our_buf;
- sc->read_frame.datalen = SOFTMIX_DATALEN(channel_read_rate, interval);
- sc->read_frame.samples = SOFTMIX_SAMPLES(channel_read_rate, interval);
+ sc->read_slin_format = slin_format;
/* Setup smoother */
- ast_slinfactory_init_with_format(&sc->factory, sc->write_frame.subclass.format);
+ ast_slinfactory_init_with_format(&sc->factory, slin_format);
/* set new read and write formats on channel. */
- ast_set_read_format(bridge_channel->chan, sc->read_frame.subclass.format);
- ast_set_write_format(bridge_channel->chan, sc->write_frame.subclass.format);
+ ast_channel_lock(bridge_channel->chan);
+ ast_set_read_format_path(bridge_channel->chan,
+ ast_channel_rawreadformat(bridge_channel->chan), slin_format);
+ ast_channel_unlock(bridge_channel->chan);
+ ast_set_write_format(bridge_channel->chan, slin_format);
/* set up new DSP. This is on the read side only right before the read frame enters the smoother. */
- sc->dsp = ast_dsp_new_with_rate(channel_read_rate);
+ sc->dsp = ast_dsp_new_with_rate(rate);
/* we want to aggressively detect silence to avoid feedback */
if (bridge_channel->tech_args.talking_threshold) {
ast_dsp_set_threshold(sc->dsp, bridge_channel->tech_args.talking_threshold);
@@ -591,6 +594,28 @@ static void softmix_bridge_write_voice(struct ast_bridge *bridge, struct ast_bri
/* Write the frame into the conference */
ast_mutex_lock(&sc->lock);
+
+ if (ast_format_cmp(frame->subclass.format, sc->read_slin_format) != AST_FORMAT_CMP_EQUAL) {
+ /*
+ * The incoming frame is not the expected format. Update
+ * the channel's translation path to get us slinear from
+ * the new format for the next frame.
+ *
+ * There is the possibility that this frame is an old slinear
+ * rate frame that was in flight when the softmix bridge
+ * changed rates. If so it will self correct on subsequent
+ * frames.
+ */
+ ast_channel_lock(bridge_channel->chan);
+ ast_debug(1, "Channel %s wrote unexpected format into bridge. Got %s, expected %s.\n",
+ ast_channel_name(bridge_channel->chan),
+ ast_format_get_name(frame->subclass.format),
+ ast_format_get_name(sc->read_slin_format));
+ ast_set_read_format_path(bridge_channel->chan, frame->subclass.format,
+ sc->read_slin_format);
+ ast_channel_unlock(bridge_channel->chan);
+ }
+
ast_dsp_silence_with_energy(sc->dsp, frame, &totalsilence, &cur_energy);
if (bridge->softmix.video_mode.mode == AST_BRIDGE_VIDEO_MODE_TALKER_SRC) {
@@ -729,15 +754,19 @@ static void gather_softmix_stats(struct softmix_stats *stats,
struct ast_bridge_channel *bridge_channel)
{
int channel_native_rate;
- int i;
+
/* Gather stats about channel sample rates. */
- channel_native_rate = MAX(ast_format_get_sample_rate(ast_channel_rawwriteformat(bridge_channel->chan)),
+ ast_channel_lock(bridge_channel->chan);
+ channel_native_rate = MAX(SOFTMIX_MIN_SAMPLE_RATE,
ast_format_get_sample_rate(ast_channel_rawreadformat(bridge_channel->chan)));
+ ast_channel_unlock(bridge_channel->chan);
- if (channel_native_rate > stats->highest_supported_rate) {
+ if (stats->highest_supported_rate < channel_native_rate) {
stats->highest_supported_rate = channel_native_rate;
}
- if (channel_native_rate > softmix_data->internal_rate) {
+ if (softmix_data->internal_rate < channel_native_rate) {
+ int i;
+
for (i = 0; i < ARRAY_LEN(stats->sample_rates); i++) {
if (stats->sample_rates[i] == channel_native_rate) {
stats->num_channels[i]++;
@@ -749,7 +778,7 @@ static void gather_softmix_stats(struct softmix_stats *stats,
}
}
stats->num_above_internal_rate++;
- } else if (channel_native_rate == softmix_data->internal_rate) {
+ } else if (softmix_data->internal_rate == channel_native_rate) {
stats->num_at_internal_rate++;
}
}
@@ -1119,8 +1148,8 @@ static int softmix_bridge_create(struct ast_bridge *bridge)
softmix_bridge_data_destroy(softmix_data);
return -1;
}
- /* start at 8khz, let it grow from there */
- softmix_data->internal_rate = 8000;
+ /* start at minimum rate, let it grow from there */
+ softmix_data->internal_rate = SOFTMIX_MIN_SAMPLE_RATE;
softmix_data->internal_mixing_interval = DEFAULT_SOFTMIX_INTERVAL;
bridge->tech_pvt = softmix_data;
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 745347cb1..ca0e5cc03 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -629,22 +629,6 @@ static struct ast_frame *chan_pjsip_read(struct ast_channel *ast)
return f;
}
- if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
- struct ast_format_cap *caps;
-
- ast_debug(1, "Oooh, format changed to %s\n", ast_format_get_name(f->subclass.format));
-
- caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- if (caps) {
- ast_format_cap_append(caps, f->subclass.format, 0);
- ast_channel_nativeformats_set(ast, caps);
- ao2_ref(caps, -1);
- }
-
- ast_set_read_format(ast, ast_channel_readformat(ast));
- ast_set_write_format(ast, ast_channel_writeformat(ast));
- }
-
if (channel->session->dsp) {
f = ast_dsp_process(ast, channel->session->dsp, f);
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 6dd3ac49c..82abe99c1 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -1953,6 +1953,21 @@ int ast_write_text(struct ast_channel *chan, struct ast_frame *frame);
int ast_prod(struct ast_channel *chan);
/*!
+ * \brief Set specific read path on channel.
+ * \since 13.4.0
+ *
+ * \param chan Channel to setup read path.
+ * \param raw_format Format to expect from the channel driver.
+ * \param core_format What the core wants to read.
+ *
+ * \pre chan is locked
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_set_read_format_path(struct ast_channel *chan, struct ast_format *raw_format, struct ast_format *core_format);
+
+/*!
* \brief Sets read format on channel chan from capabilities
* Set read format for channel to whichever component of "format" is best.
* \param chan channel to change
diff --git a/main/channel.c b/main/channel.c
index 6aca21ac6..af9395616 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -4114,78 +4114,133 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
ast_frfree(f);
f = &ast_null_frame;
}
- } else if (f->frametype == AST_FRAME_VOICE && ast_format_cap_iscompatible_format(ast_channel_nativeformats(chan), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
- /* This frame is not one of the current native formats -- drop it on the floor */
- struct ast_str *codec_buf = ast_str_alloca(64);
- ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n",
- ast_channel_name(chan), ast_format_get_name(f->subclass.format), ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf));
- ast_frfree(f);
- f = &ast_null_frame;
- } else if (f->frametype == AST_FRAME_VOICE) {
- /* Send frame to audiohooks if present */
- if (ast_channel_audiohooks(chan)) {
- struct ast_frame *old_frame = f;
- f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
- if (old_frame != f)
- ast_frfree(old_frame);
+ break;
+ }
+ if (f->frametype != AST_FRAME_VOICE) {
+ break;
+ }
+ if (ast_format_cmp(f->subclass.format, ast_channel_rawreadformat(chan)) != AST_FORMAT_CMP_EQUAL
+ && ast_format_cmp(f->subclass.format, ast_channel_readformat(chan)) != AST_FORMAT_CMP_EQUAL) {
+ struct ast_format *core_format;
+
+ /*
+ * Note: This frame may not be one of the current native
+ * formats. We may have gotten it out of the read queue from
+ * a previous multi-frame translation, from a framehook
+ * injected frame, or the device we're talking to isn't
+ * respecting negotiated formats. Regardless we will accept
+ * all frames.
+ *
+ * Update the read translation path to handle the new format
+ * that just came in. If the core wants slinear we need to
+ * setup a new translation path because the core is usually
+ * doing something with the audio itself and may not handle
+ * any other format. e.g., Softmix bridge, holding bridge
+ * announcer channel, recording, AMD... Otherwise, we'll
+ * setup to pass the frame as is to the core. In this case
+ * the core doesn't care. The channel is likely in
+ * autoservice, safesleep, or the channel is in a bridge.
+ * Let the bridge technology deal with format compatibility
+ * between the channels in the bridge.
+ *
+ * Beware of the transcode_via_slin and genericplc options as
+ * they force any transcoding to go through slin on a bridge.
+ * Unfortunately transcode_via_slin is enabled by default and
+ * genericplc is enabled in the codecs.conf.sample file.
+ *
+ * XXX Only updating translation to slinear frames has some
+ * corner cases if slinear is one of the native formats and
+ * there are different sample rates involved. We might wind
+ * up with conflicting translation paths between channels
+ * where the read translation path on this channel reduces
+ * the sample rate followed by a write translation path on
+ * the peer channel that increases the sample rate.
+ */
+ core_format = ast_channel_readformat(chan);
+ if (!ast_format_cache_is_slinear(core_format)) {
+ core_format = f->subclass.format;
+ }
+ if (ast_set_read_format_path(chan, f->subclass.format, core_format)) {
+ /* Drop frame. We couldn't make it compatible with the core. */
+ ast_frfree(f);
+ f = &ast_null_frame;
+ break;
}
- if (ast_channel_monitor(chan) && ast_channel_monitor(chan)->read_stream ) {
- /* XXX what does this do ? */
+ }
+ /* Send frame to audiohooks if present */
+ if (ast_channel_audiohooks(chan)) {
+ struct ast_frame *old_frame = f;
+
+ f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
+ if (old_frame != f) {
+ ast_frfree(old_frame);
+ }
+ }
+ if (ast_channel_monitor(chan) && ast_channel_monitor(chan)->read_stream) {
+ /* XXX what does this do ? */
#ifndef MONITOR_CONSTANT_DELAY
- int jump = ast_channel_outsmpl(chan) - ast_channel_insmpl(chan) - 4 * f->samples;
- if (jump >= 0) {
- jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)),
- ast_format_get_sample_rate(f->subclass.format),
- ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
- if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump, SEEK_FORCECUR) == -1) {
- ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
- }
- ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + (ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)) + f->samples);
- } else {
- ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + f->samples);
+ int jump = ast_channel_outsmpl(chan) - ast_channel_insmpl(chan) - 4 * f->samples;
+ if (jump >= 0) {
+ jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)),
+ ast_format_get_sample_rate(f->subclass.format),
+ ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
+ if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump, SEEK_FORCECUR) == -1) {
+ ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
}
+ ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + (ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)) + f->samples);
+ } else {
+ ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + f->samples);
+ }
#else
- int jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)),
- ast_format_get_sample_rate(f->subclass.codec),
- ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
- if (jump - MONITOR_DELAY >= 0) {
- if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump - f->samples, SEEK_FORCECUR) == -1)
- ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
- ast_channel_insmpl(chan) += ast_channel_outsmpl(chan) - ast_channel_insmpl(chan);
- } else
- ast_channel_insmpl(chan) += f->samples;
-#endif
- if (ast_channel_monitor(chan)->state == AST_MONITOR_RUNNING) {
- if (ast_writestream(ast_channel_monitor(chan)->read_stream, f) < 0)
- ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
+ int jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)),
+ ast_format_get_sample_rate(f->subclass.codec),
+ ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
+ if (jump - MONITOR_DELAY >= 0) {
+ if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump - f->samples, SEEK_FORCECUR) == -1) {
+ ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
}
+ ast_channel_insmpl(chan) += ast_channel_outsmpl(chan) - ast_channel_insmpl(chan);
+ } else {
+ ast_channel_insmpl(chan) += f->samples;
}
+#endif
+ if (ast_channel_monitor(chan)->state == AST_MONITOR_RUNNING) {
+ if (ast_writestream(ast_channel_monitor(chan)->read_stream, f) < 0)
+ ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
+ }
+ }
- if (ast_channel_readtrans(chan) && (f = ast_translate(ast_channel_readtrans(chan), f, 1)) == NULL) {
+ if (ast_channel_readtrans(chan)
+ && ast_format_cmp(f->subclass.format, ast_channel_rawreadformat(chan)) == AST_FORMAT_CMP_EQUAL) {
+ f = ast_translate(ast_channel_readtrans(chan), f, 1);
+ if (!f) {
f = &ast_null_frame;
}
+ }
- /* it is possible for the translation process on chan->readtrans to have
- produced multiple frames from the single input frame we passed it; if
- this happens, queue the additional frames *before* the frames we may
- have queued earlier. if the readq was empty, put them at the head of
- the queue, and if it was not, put them just after the frame that was
- at the end of the queue.
- */
- if (AST_LIST_NEXT(f, frame_list)) {
- if (!readq_tail) {
- ast_queue_frame_head(chan, AST_LIST_NEXT(f, frame_list));
- } else {
- __ast_queue_frame(chan, AST_LIST_NEXT(f, frame_list), 0, readq_tail);
- }
- ast_frfree(AST_LIST_NEXT(f, frame_list));
- AST_LIST_NEXT(f, frame_list) = NULL;
+ /*
+ * It is possible for the translation process on the channel to have
+ * produced multiple frames from the single input frame we passed it; if
+ * this happens, queue the additional frames *before* the frames we may
+ * have queued earlier. if the readq was empty, put them at the head of
+ * the queue, and if it was not, put them just after the frame that was
+ * at the end of the queue.
+ */
+ if (AST_LIST_NEXT(f, frame_list)) {
+ if (!readq_tail) {
+ ast_queue_frame_head(chan, AST_LIST_NEXT(f, frame_list));
+ } else {
+ __ast_queue_frame(chan, AST_LIST_NEXT(f, frame_list), 0, readq_tail);
}
-
- /* Run generator sitting on the line if timing device not available
- * and synchronous generation of outgoing frames is necessary */
- ast_read_generator_actions(chan, f);
+ ast_frfree(AST_LIST_NEXT(f, frame_list));
+ AST_LIST_NEXT(f, frame_list) = NULL;
}
+
+ /*
+ * Run generator sitting on the line if timing device not available
+ * and synchronous generation of outgoing frames is necessary
+ */
+ ast_read_generator_actions(chan, f);
break;
default:
/* Just pass it on! */
@@ -5034,29 +5089,35 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
}
/* If the frame is in the raw write format, then it's easy... just use the frame - otherwise we will have to translate */
- if (ast_format_cmp(fr->subclass.format, ast_channel_rawwriteformat(chan)) != AST_FORMAT_CMP_NOT_EQUAL) {
+ if (ast_format_cmp(fr->subclass.format, ast_channel_rawwriteformat(chan)) == AST_FORMAT_CMP_EQUAL) {
f = fr;
} else {
- if ((ast_format_cap_iscompatible_format(ast_channel_nativeformats(chan), fr->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) &&
- (ast_format_cmp(ast_channel_writeformat(chan), fr->subclass.format) != AST_FORMAT_CMP_EQUAL)) {
- struct ast_str *codec_buf = ast_str_alloca(64);
+ if (ast_format_cmp(ast_channel_writeformat(chan), fr->subclass.format) != AST_FORMAT_CMP_EQUAL) {
+ struct ast_str *codec_buf = ast_str_alloca(256);
/*
- * XXX Something is not right. We are not compatible with this
- * frame. Bad things can happen. Problems range from no audio,
- * one-way audio, to unexplained line hangups. As a last resort
- * try to adjust the format. Ideally, we do not want to do this
- * because it indicates a deeper problem. For now, we log these
- * events to reduce user impact and help identify the problem
- * areas.
+ * We are not setup to write this frame. Things may have changed
+ * on the peer side of the world and we try to adjust the format to
+ * make it compatible again. However, bad things can happen if we
+ * cannot setup a new translation path. Problems range from no
+ * audio, one-way audio, to garbled audio. The best we can do is
+ * request the call to hangup since we could not make it compatible.
+ *
+ * Being continuously spammed by this message likely indicates a
+ * problem with the peer because it cannot make up its mind about
+ * which format to use.
*/
- ast_log(LOG_WARNING, "Codec mismatch on channel %s setting write format to %s from %s native formats %s\n",
- ast_channel_name(chan), ast_format_get_name(fr->subclass.format), ast_format_get_name(ast_channel_writeformat(chan)),
+ ast_debug(1, "Channel %s changing write format from %s to %s, native formats %s\n",
+ ast_channel_name(chan),
+ ast_format_get_name(ast_channel_writeformat(chan)),
+ ast_format_get_name(fr->subclass.format),
ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf));
- ast_set_write_format(chan, fr->subclass.format);
+ if (ast_set_write_format(chan, fr->subclass.format)) {
+ /* Could not handle the new write format. Induce a hangup. */
+ break;
+ }
}
-
- f = (ast_channel_writetrans(chan)) ? ast_translate(ast_channel_writetrans(chan), fr, 0) : fr;
+ f = ast_channel_writetrans(chan) ? ast_translate(ast_channel_writetrans(chan), fr, 0) : fr;
}
if (!f) {
@@ -5216,6 +5277,42 @@ done:
return res;
}
+int ast_set_read_format_path(struct ast_channel *chan, struct ast_format *raw_format, struct ast_format *core_format)
+{
+ struct ast_trans_pvt *trans_old;
+ struct ast_trans_pvt *trans_new;
+
+ if (ast_format_cmp(ast_channel_rawreadformat(chan), raw_format) == AST_FORMAT_CMP_EQUAL
+ && ast_format_cmp(ast_channel_readformat(chan), core_format) == AST_FORMAT_CMP_EQUAL) {
+ /* Nothing to setup */
+ return 0;
+ }
+
+ ast_debug(1, "Channel %s setting read format path: %s -> %s\n",
+ ast_channel_name(chan),
+ ast_format_get_name(raw_format),
+ ast_format_get_name(core_format));
+
+ /* Setup new translation path. */
+ if (ast_format_cmp(raw_format, core_format) != AST_FORMAT_CMP_EQUAL) {
+ trans_new = ast_translator_build_path(core_format, raw_format);
+ if (!trans_new) {
+ return -1;
+ }
+ } else {
+ /* No translation needed. */
+ trans_new = NULL;
+ }
+ trans_old = ast_channel_readtrans(chan);
+ if (trans_old) {
+ ast_translator_free_path(trans_old);
+ }
+ ast_channel_readtrans_set(chan, trans_new);
+ ast_channel_set_rawreadformat(chan, raw_format);
+ ast_channel_set_readformat(chan, core_format);
+ return 0;
+}
+
struct set_format_access {
const char *direction;
struct ast_trans_pvt *(*get_trans)(const struct ast_channel *chan);
@@ -6209,15 +6306,17 @@ static int ast_channel_make_compatible_helper(struct ast_channel *from, struct a
RAII_VAR(struct ast_format *, best_dst_fmt, NULL, ao2_cleanup);
int no_path;
- ast_channel_lock_both(from, to);
+ /*
+ * We cannot short circuit this code because it is possible to ask
+ * to make compatible two channels that are "compatible" because
+ * they already have translation paths setup but together make for
+ * a sub-optimal path. e.g., The From channel has g722 -> ulaw
+ * and the To channel has ulaw -> g722. They are "compatible" but
+ * together the translations are unnecessary and the audio loses
+ * fidelity in the process.
+ */
- if ((ast_format_cmp(ast_channel_readformat(from), ast_channel_writeformat(to)) != AST_FORMAT_CMP_NOT_EQUAL) &&
- (ast_format_cmp(ast_channel_readformat(to), ast_channel_writeformat(from)) != AST_FORMAT_CMP_NOT_EQUAL)) {
- /* Already compatible! Moving on ... */
- ast_channel_unlock(to);
- ast_channel_unlock(from);
- return 0;
- }
+ ast_channel_lock_both(from, to);
src_cap = ast_channel_nativeformats(from); /* shallow copy, do not destroy */
dst_cap = ast_channel_nativeformats(to); /* shallow copy, do not destroy */
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 2ecd11a84..7756179b3 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -252,8 +252,8 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi
/* get the joint capabilities between peer and endpoint */
ast_format_cap_get_compatible(caps, peer, joint);
if (!ast_format_cap_count(joint)) {
- struct ast_str *usbuf = ast_str_alloca(64);
- struct ast_str *thembuf = ast_str_alloca(64);
+ struct ast_str *usbuf = ast_str_alloca(256);
+ struct ast_str *thembuf = ast_str_alloca(256);
ast_rtp_codecs_payloads_destroy(&codecs);
ast_log(LOG_NOTICE, "No joint capabilities for '%s' media stream between our configuration(%s) and incoming SDP(%s)\n",
@@ -269,33 +269,22 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi
ast_format_cap_append_from_cap(session->req_caps, joint, AST_MEDIA_TYPE_UNKNOWN);
if (session->channel) {
- struct ast_format *fmt;
-
ast_channel_lock(session->channel);
ast_format_cap_remove_by_type(caps, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_append_from_cap(caps, ast_channel_nativeformats(session->channel), AST_MEDIA_TYPE_UNKNOWN);
+ ast_format_cap_append_from_cap(caps, ast_channel_nativeformats(session->channel),
+ AST_MEDIA_TYPE_UNKNOWN);
ast_format_cap_remove_by_type(caps, media_type);
-
- /*
- * XXX Historically we picked the "best" joint format to use
- * and stuck with it. It would be nice to just append the
- * determined joint media capabilities to give translation
- * more formats to choose from when necessary. Unfortunately,
- * there are some areas of the system where this doesn't work
- * very well. (The softmix bridge in particular is reluctant
- * to pick higher fidelity formats and has a problem with
- * asymmetric sample rates.)
- */
- fmt = ast_format_cap_get_format(joint, 0);
- ast_format_cap_append(caps, fmt, 0);
+ ast_format_cap_append_from_cap(caps, joint, media_type);
/*
* Apply the new formats to the channel, potentially changing
* raw read/write formats and translation path while doing so.
*/
ast_channel_nativeformats_set(session->channel, caps);
- ast_set_read_format(session->channel, ast_channel_readformat(session->channel));
- ast_set_write_format(session->channel, ast_channel_writeformat(session->channel));
+ if (media_type == AST_MEDIA_TYPE_AUDIO) {
+ ast_set_read_format(session->channel, ast_channel_readformat(session->channel));
+ ast_set_write_format(session->channel, ast_channel_writeformat(session->channel));
+ }
if ((session->endpoint->dtmf == AST_SIP_DTMF_AUTO)
&& (ast_rtp_instance_dtmf_mode_get(session_media->rtp) == AST_RTP_DTMF_MODE_RFC2833)
&& (session->dsp)) {
@@ -309,8 +298,6 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi
}
}
ast_channel_unlock(session->channel);
-
- ao2_ref(fmt, -1);
}
ast_rtp_codecs_payloads_destroy(&codecs);