Asterisk1.8 转码策略分析

最近在修改asterisk转码和编码协商的问题,发现asterisk的转码策略的选择还是有些问题的(基于1.8.9.3版本)。
——————————————
相关的CLI命令
转码路径的调试命令:
core show channels
core show channel ${CHANNEL}

查看不同编码之间进行转换的时间开销:
core show translation

查看某种编码转换为其它编码的路径:
core show translation paths {codec}
eg: core show translation paths ulaw


ast_channel中与转码相关的数据成员:
ast_channel->nativeformats
ast_channel->writeformat
ast_channel->readformat
ast_channel->rawwriteformat
ast_channel->rawreadformat
ast_channel->writetrans
ast_channel->readtrans
——————————————
以下是测试用的case:
phone A: PCMU phone B:GSM
user A:PCMU,GSM user B:PCMA,GSM

对于该用例asterisk的转码路径是这样的。
1.phone A => phone B
channel A读转码(PCMU => PCMA)
channel B写转码(PCMA => SLINEAR => GSM)
2.phone B => phone A
channel B读转码(GSM => SLINEAR => PCMU)
channel A写转码(PCMU,无需转码)

codec_trans.png从主叫到被叫与从被叫到主叫的转码路径是不一致的,前者比后者多了一次从 PCMU 到 PCMA 的转换。为什么会出现这种情况呢?

打开log开关、结合CLI命令进行分析,开始看代码。

asterisk对于是否需要转码及转码策略的选择是在ast_channel_make_compatible中做的。该函数又调用ast_channel_make_compatible_helper来设置从主叫到被叫及被叫到主叫的转码策略。

在ast_set_read_format中,先调用ast_translator_best_choice(&fmt,&native)从native(即对应通道的nativeformates)和fmt(要设置的编码集合)中分别选择最优的一种编码,并将选择出的编码重新赋值给native和fmt,这里是传地址的,通过指针修改。然后把native的值赋值给rawformat(通道的rawreadformat),将fmt的值赋值给format(通道的readformat)。最后,如果format与native值不相同的话,就调用ast_translator_build_path(*format, *rawformat)来建立转码路径的链表。ast_set_write_format与ast_set_read_format同理,只不过fmt是赋值给通道的writeformat,native是赋值给通道的rawwriteformat。

收到200 OK后,会调用process_sdp(file:channels/chan_sip.c)来解析SDP,在处理被叫终端的SDP时,被叫通道的nativeformats可能会改变,此时需要重新设置被叫通道的读写转码路径,对于writetranscode: 从channel->writeformat到channel->nativeformats进行转换,对于readtranscode: 从channel->nativeformats到channel->readformat进行转换。被叫通道的writeformat和readformat在之前调用ast_channel_make_compatible时已经被设置过了。

再回到我们的问题上来。经过分析,出现该问题的原因是在收到被叫的200 OK之前channelB->nativeformats的音频编码是Ulaw,而在收到200 OK后,channelB->nativeformats的音频编码是G729,而在通道桥接时,并没有检查channelB->nativeformats是否发生变化,没有去更改channelB->writeformat(例子中为ulaw)和channelA->readformat(例子中为ulaw),使主叫仍按照之前选择的路径去进行转码,从而导致了不必要的转码步骤。

另外,在asterisk关于转码的实现中,主叫和被叫通道都分别有 WriteTranscode和ReadTranscode(可以在通话时通过core show channel sip/{EXTEN}查看),这样每一路通话最多可能会用到四个转码资源(ast_trans_pvt)。针对这些问题,我修改后的做法是:只在被叫通道一侧做转码,主叫、被叫通道的readformat与writeformat设置为主叫通道的nativeformats(主叫的nativeformats是不会改变的),这样主叫通道不需要分配转码资源,最多只占用两个转码资源。并且如果在处理完被叫终端回复的SDP后被叫通道的nativeformats改变了,不需要对主叫和被叫的readformat和writeformat重新设置,只需要重新设置被叫通道的读写转码路径即可(具体修改见以上代码中modify和add部分)。

ast_channel_make_compatible这个函数只是检查呼叫中的两个channel的数据结构的一些编码成员并设置两个channel之间的编码路径。实际的转码是桥接时(如:ast_generic_bridge)在ast_read或ast_write中进行的,在读写帧之前调用ast_translate按照设置好的转码

原文地址:https://www.cnblogs.com/lijingcheng/p/4454904.html