FreeSwitch 编码协商

官网原文https://freeswitch.org/confluence/display/FREESWITCH/Codec+Negotiation

 

摘要

        编码协商可能是一个令人困惑的主题。如果您不熟悉SDP(会话描述协议),那么这就增加了一层额外的神秘。如果你对这个话题感到陌生(或者只是对你所读过的和经历的东西感到困惑),那么希望通过这个简短的介绍,事情会变得更清楚一些。

        首先,我们所说的“编解码协商”是什么意思?我们正在讨论是:每条腿将选择什么编码(codec)的处理过程。FreeSWITCH支持许多种编码。大部分的SIP终端也支持多种编码。而每条腿只能使用一种编码,因此,编码协商过程就是筛选过滤并确定最终选用编码的过程。这种“筛选过程”会引起混淆。这个过程是如何工作的?又如何修改它?本文将帮你回答这些问题。

FreeSWITCH的编码协商

        FreeSWITCH支持两种基本的编解码协商模式:早期协商和晚期协商。早期协商意味着在FreeSWITCH和终端之间尽快协商编码,甚至在FreeSWITCH需要发送媒体(如铃声)或响应呼叫之前。这个过程甚至在来电到触发拨号计划之前就发生了。晚期协商意味着延迟编解码的选择处理,直到呼叫触发拨号计划之后,(能够)收集到更多的信息时才协商编码。这些附加信息可用于影响协商过程。我们接下来说明早期协商和晚期协商之间的区别。

 

考虑一下这个场景:

  • Alice有一台支持G722和PCMU的电话
  • Bob有一台支持PCMU和PCMA的电话
  • FreeSWITCH针对外呼的编码偏好列表是:G722,G722.1,PCMU,PCMA,G729
    (FS的这个编码表将提供给所有的外呼通话)

观察以下两个呼叫流程,看你能否发现A腿编码协商处理的差异:

  • 早期协商

Alice (A)拨打Bob (B)的号码

A呼叫FS并提供两种编码:G722和PCMU

FS检查A的编码表,并立即为A腿选择了G722编码

FS呼叫B并提供编码表:G722,G722.1,PCMU,PCMA,G729

B检视编码表,并选择PCMU

FS将A和B桥接在一起

FS执行G722和PCMU的编码转换

 

  • 携带“继承编码”集的晚期协商

Alice (A)拨打Bob (B)的号码

A呼叫FS并提供两种编码:G722和PCMU

FS检视这两种编码,但没有马上选择其中的一种

FS呼叫B并提供可选编码G722和PCMU

B检视编码集并选择PCMU

此时FS为A腿选择PCMU编码

FS将A和B桥接到一块

A与B通过PCMU通信(不需要额外的编码转换)

        注意A腿编码协商在时间上的差异。早期协商模式下,FreeSWITCH立刻选择一种编码(G722)。它并不等待和关心外呼(B腿)过程发生了什么,因此A腿直接选择722编码。在A腿决定编码后,FreeSWITCH通过拨号计划传递这个呼叫,最终通过一个bridge app呼叫Bob的电话。为了初始化bridge,FreeSWITCH呼叫了B腿,并提供了一个相当大的编码表,这个表来源于FreeSWITCH的外呼编码偏好配置(这个表是可自定义的,详情后叙)。Bob的电话看到这个编码表,并从中选择第一个匹配的编码,在本例中就是PCMU。这时,双边的编码协商结束。

  • A 腿使用 G722
  • B 腿使用 PCMU

        Bob的电话一回振铃信号,两条腿就被桥接在一起。FreeSWITCH必须将Alice的编解码器(G 722)转换为Bob的编解码器(PCMU),因为协商的结果是两种不同的编码。通常情况下,这是没问题的,但假设两部话机支持的编码集有交集,那你可能希望最终协商出相同的编码以减少CPU的消耗。这就是晚期协商起作用的地方。

        如您所见,当FreeSWITCH为A腿选择一种编码而不知道B腿上设置了什么编码时,就很容易出现“编码失配”。编码“不匹配”并不总是坏事,但很多时候,让两条腿共享相同的编码是件好事。使用晚期协商和一种名为“继承编码”的技术,我们可以强制A腿使用B腿上协商的编码。(这个过程稍后会详细描述,目前我们只考虑基本概念。)

        在我们的第二个例子中,基本的呼叫流是FreeSWITCH创建A腿,但不是立即协商编解码。相反,FreeSWITCH在执行拨号计划之后才开始协商A腿的编码。在这种情况下,拨号计划执行结果是桥接Bob的电话。在桥接过程中,FreeSWITCH与Bob的电话协商了一种编码器即PCMU。一旦在B腿上选择了编码,FreeSWITCH就回到Alice的话机上,告诉它我们将在A腿上使用PCMU。现在呼叫的两条腿就使用相同的编码,这正是我们想要的。

        这就是早期协商和晚期协商的区别所在。请记住:简单地启用晚期协商并不会自动强制A腿继承B腿的编码。它只是允许“继承编码”作为协商编码的一种方式。晚期协商允许使用其他编码协商技巧。更多详情,请继续阅读。

早期协商

        这是缺省的行为模式。

一般原则

 

  • 当A腿呼叫FreeSWITCH时,它所提供的编码表将与相关SIP配置文件中的inbound-codec-prefs内容进行比较。一旦A提供的某个编码与FS允许的编码匹配,就会选择它为A腿的编码。如果A提供的编码中没有一个与允许的编码相匹配,则呼叫失败。

注:由于算法的原因,入局SDP中的编码顺序相对于inbound-codec-prefs配置的编码顺序具体优先权。

  • 当FS呼叫B腿时,SIP配置项outbound-codec-prefs中的编码表被重新组织,方法是将上面A腿协商的编码推到表的顶部。显然,如果B不能接受编码表,结果就是呼叫失败。

例如:

A -------- GSM/PCMA/G729 --------> FS (allowing G729/PCMA/PCMU) -------- PCMA/G729/PCMU --------> B

发生了什么?

  • A向FS建议编码表GSM/PCMA/G729
  • FS根据其允许的编码表(inbound-codec-prefs中配置)按优先次序(SDP中的先后顺序)检查建议的编码表,并选择PCMA作为第一个授权编码,因此编解码表调整为:PCMA/G 729/PCMU
  • FS将调整后的编码表提交给B

早期协商参数

  • disable-transcoding

这是一个可以在出局SIP配置文件中设置的参数。
它将强制B腿(出局)使用A腿(入局)协商结果相同的编码。

若要设置此参数,请将以下行添加到所需的SIP配置文件中

<param name="disable-transcoding" value="true"/>

注意:通常误解的是,这个参数禁用FS中的编码转换能力。这是错误的。

这个参数只是更改出局编码以匹配入局端上的协商结果,从而达到不需转码的目的。

通过设置absolute_codec_string变量值为入局编码,也能达到相同的目的。

这个参数(仅在关闭晚期协商时适用)将从A腿获取协商后的编码,并将其作为在B腿的唯一选项(忽略任何其他编码设置),如果B腿不能使用相同的编码,则呼叫失败。(笔者实测,实际上在晚期协商模式下也是生效的)

  • absolute_codec_string

这是一个通道变量,可以在拨号计划中设置,通常是在桥接前设置。
这个变量将强制B腿使用变量值指定的编码列表,而不用考虑其它任何因素。

例如:

<action application="export" data="nolocal:absolute_codec_string=PCMA,PCMU"/>
<action application="bridge" data="sofia/gateway/mygateway/mynumber"/>

或者

<action application="bridge" data="{absolute_codec_string='PCMA,PCMU'}sofia/gateway/mygateway/mynumber"/>

在后一种表达式中,请在编码表加上一对单引号,以保护参数解析的完整性,以逗号( ',' )为编码分割符,比如{var1=val1,var2=val2,absolute_codec_string='GSM,PCMU'}

注意:这个通道变量适用于disable-transcoding变量。换句话说,如果在出局SIP配置文件设置了disable-transcoding变量,并且在拨号计划中设置absolute_codec_string

为有别于入局编码的值,那么依然会产生转码开销。

  • codec_string

这是一个通道变量,可以在拨号计划中设置,通常是在桥接前设置。
The defined codec list will override the one set in the outbound-codec-prefs parameter of the outbound profile.
这个变量定义的编码列表将覆盖出局配置文件的 outbound-codec-prefs参数中设置的值。
以下是一个例子:

<action application="export" data="nolocal:codec_string=PCMA,PCMU"/>
<action application="bridge" data="sofia/gateway/mygateway/mynumber"/>

或者:

<action application="bridge" data="{codec_string='PCMA,PCMU'}sofia/gateway/mygateway/mynumber"/>

早期协商+禁用转码:

<param name="disable-transcoding" value="true"/>

        如果sofia配置文件中设置了这个参数,那么所有呼出到B腿的信令中,将只携带A腿协商结果(只带一种编码,优先级最高的那个)

在任何一种情况下,A腿上的变量“codec_string”控制提供给B腿的编码集。
变量“absolute_codec_string”类似,但它暗示了隐式的编码列表,并将禁用一种默认行为--将A腿编码添加到列表中。

晚期协商(需要的参数)

  • inbound-late-negotiation
<param name="inbound-late-negotiation" value="true"/>
  • 呼叫将直接执行拨号计划而不需要先查看编码。
  • 协商将在A腿被应答后进行。
  • 允许你将呼叫路由到一个脚本,检查SDP,并通过"codec_string"通道变量重写可接受的编码表。
  • inherit_codec
<action application="set" data="inherit_codec=true"/>

inherit_codec=true (仅在启用晚期协商时才适用)将在B腿应答后触发编码协商,界时B腿的协商结果将会传递给A腿,以便两边使用相同的编码(如果A腿支持它);否则,它将从自己的列表中使用它所能使用的一切

只有在配置文件上启用晚期协商时,此变量才可用。它是一个可读字符串,包含主叫终端提取的所有编码。这可以很容易地从拨号计划中解析出来。

<action application="export" data="codec_string='${ep_codec_string}'"/>

代理媒体

        如果启用代理媒体并且没有执行过answer app,那么将无法进行编码协商。

不相容宿端

        如下所示,在需要混频/双腿编码不同、特别是激活media_mix_inbound_outbound_codecs变量的场景,媒体代理都不适用。

混频/双腿编码不同(需要转码)

        如果您希望FreeSWITCH能够将两条腿与不同的编码匹配(在其中进行编码转换),那么需要设置几个变量才能使其成为可能。首先,您必须设置media_mix_inbound_outbound_codecs=true,既可以全局设置(通过ie vars.xml),也可以通过bridge变量单独设置(例如:<action application="bridge" data="{media_mix_inbound_outbound_codecs=true}sofia/gateway/...."> )。

        这个变量在V1.6及更高版本中是必须的,这是由于WebRTC支持,以及UDP承载SIP时SDP体量不断增长的原因。由于SDP的剪切尺寸,使得从WebRTC到SIP转换成原始SDP变得越来越困难。基于UDP承载SIP时,一旦出现分包,就会破坏SIP数据包,因为UDP不会进行自动分包重组。当SIP包大于MTU值时,就会产生分包,唯一的解决方案就是减小包的大小。这意味着压缩报头、消除额外的报头、或者删减不需要的编码描述以裁减SDP包体大小。

        这意味着紧凑的头,消除额外的头,或只是不使用这么大的SDP消除编解码你不需要。对FS的影响是:如果没有将media_mix_inbound_outbound_codecs设置为true,则只有A腿提供给FS的编码会被传递给B腿,这会减小SDP的体量,并大大减小了转码所带来的副作用。

        其次,你可能希望关闭inbound-late-negotiationinbound-zrtp-passthru (第二个强制第一个为true),或者希望在桥接之前预先应答A腿的呼叫。

        最后,您应该确保这些变量,disable-transcoding,inherit_codec,bypass_mediaproxy_media,统统设置为false (因为这些都与我们想要实现的目标无关)。

警告

        如果您的sdp包含具有不同ptime首选项的编码,并且ptime在sdp中指定,那么sofia将不会发送ptime属性

 2010-10-18 11:47:52.234322 [WARNING] sofia_glue.c:213 Codec G723 payload 4 added to sdp wanting ptime 30 but it's already 20 (G729:18:20), disabling ptime.

说明实例

使用代理媒体模式时修改编码

         下面是一个dialplan示例,它允许您更改编码首选顺序,同时仍然享受proxy_media模式的低cpu使用率。它类似于openser/opensips/kamailio 的textops模块中的subst()。

<context>
  <extension name="sdp_mangler">
    <!-- Set some defaults for this call -->
    <condition field="destination_number" expression="^(\d+)$" break="on-false">
      <action application="set" data="transfer_to=${destination_number} XML fail"/>
    </condition>
 
    <!-- Here we check for G729 if found we are going make sure we have it first in preference order -->
    <condition field="${switch_r_sdp}" expression="/(.*)(m=audio \d+ RTP\/AVP)(?=[ \d]+18|18[ \d]+)([ \d]*)(.*)/s" break="never">
      <action application="set" data="codec_mangle= 18"/>
      <action application="set" data="transfer_to=${destination_number} XML public_proxy"/>
    </condition>
 
    <!-- Here we check for PCMU to add it back into the SDP if we want -->
    <condition field="${switch_r_sdp}" expression="/(.*)(m=audio \d+ RTP\/AVP)(?=[ \d]+0|0[ \d]+)([ \d]*)(.*)/s" break="never">
      <action application="set" data="codec_mangle=${codec_mangle} 0"/>
    </condition>
 
    <!-- Here we rebuild our SDP Moving G729 up front-->
    <condition field="${switch_r_sdp}" expression="/(.*)(m=audio \d+ RTP\/AVP)([ \d]+)(.*)/s" break="never">
      <action application="set" data="switch_r_sdp=$1$2${codec_mangle} 101$4"/>
    </condition>
 
    <condition>
      <action application="transfer" data="${transfer_to}"/>
    </condition>
  </extension>
</context>

重写SDP

switch_r_sdp:
<action application="set">
    <![CDATA[switch_r_sdp=(sdp here)]]>
</action>

出局禁用G.729b

<extension name="disable-annexB" continue="true">
  <condition field="${switch_r_sdp}" expression="/(.*)(m=audio \d+ RTP\/AVP)(.*)( 18 )(.*)/s">
     <action application="export" data="rtp_append_audio_sdp=a=fmtp:18 annexb=no"/>
  </condition>
</extension>

启用代理媒体时的编码协商

  • 正确方式
<extension name="test_proxy_media">
  <condition field="source" expression="mod_sofia">
    <action application="answer"/>
    <action application="playback" data="ivr/ivr-welcome_to_freeswitch.wav"/>
  </condition>
</extension>
  • 错误方式
<extension name="test_proxy_media">
  <condition field="source" expression="mod_sofia">
    <action application="playback" data="ivr/ivr-welcome_to_freeswitch.wav"/>
  </condition>
</extension>

 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值