diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/channel.c | 267 |
1 files changed, 183 insertions, 84 deletions
diff --git a/main/channel.c b/main/channel.c index 934dd6651..a9fe7e358 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); @@ -6210,15 +6307,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 */ |