吴伟贤のBlog

Feed Rss

FreeSWITCH 编码协商

07.20.2013, freeswitch, by .

转自:http://www.8000hz.com/archives/113.html

 

编码协商

介绍:
编码协商是一个比较令人困惑的主题. 如果你不熟悉SDP(会话描述协议),那么就更加多了一层神秘的面纱. 如果你对编码协商不太熟悉(或你对你所读到的和经历的有些困惑), 下面的这个简短的介绍也许对你有些许帮助.

首先, 当我们谈编码协商时是在谈什么?  我们在讨论通话中的每一个leg选择使用哪个编码的过程. FreeSWITCH支持很多种编码.大部分的SIP终端也是支持多种编码的. 每一个leg只能使用一种编码, 所以编码协商的过程中会进行筛选和选择要使用的一个编码.正是这个筛选过程会导致混淆. 这个过程是如何工作的? 如何才能对它进行修改?  本章内容就是帮你来解决这些疑惑.

FreeSWITCH的编码协商

FreeSWITCH支持编码协商的两种模式: 早期(early)延后(late).

早期协商  就表示, FreeSWITCH和终端间的编码协商越早越好, 甚至早于FreeSWITCH发送媒体前(像振铃)或接通呼叫. 早期协商在一通电话还没有进入拨号方案前就进行.

延后协商  表示直到进入到拨号方案中再进行协商, 这样就有更多的信息可以收集. 这些额外的信息可以用于影响协商过程. 让我们演示早期协商和延后协商的区别.

假设有这个环境:

—-   Alice的电话支持G722和PCMU
—-   Bob的电话支持 PCMU和PCMA
—-   FreeSWITCH 的outbound “codec prefs” 是 G722, G722.1,PCMU,PCMA,G729
(这个是FS在外呼时携带的编码列表)

看看下面的这两个呼叫流程. 瞧瞧你是否能搞懂何时A Leg进行协商:

Early Negotiation(早期协商)

Alice(A) 拨打 Bob(B)

A 呼叫FS, 提供两种编码: G722和PCMU
FS看到这两个编码后, 立即为A leg 选择G722编码
FS呼叫B, 提供G722, G722.1, PCMU,PCMA,G729
B看到这些编码后, 选择了PCMU
FS接通A和B
FS会在G722和PCMU之间进行转码

延后协商, 同时设置了 inherit codec 

Alice(A) 拨打 Bob(B)

A 呼叫FS, 提供两种编码: G722和PCMU
FS看到这两个编码后, 并不会立即选择一个.
FS呼叫B, 提供G722和PCMU两种编码
B看到这两种编码后选择PCMU
FS现在为A leg选择PCMU
FS 接通A和B
A和B使用PCMU进行通讯(没有额外的编解码)

注意, 不同点就是在于何时决定A leg的编码.当使用早期协商时,FreeSWITCH会立即选择一个codec(G722). 它并不会关心外呼的leg(B leg). 所以A leg  就使用G722. 在决定好Aleg使用的编码后, FreeSWITCH 将通话进到拨号方案中, 当然最终会使用bridge到bob的电话. 要桥接呼叫, FreeSWITCH呼叫B leg然后提供一个比较大的编码列表. 这个列表就是FreeSWITCH的”outbound codec prefs”.(这个列表可以自己定义, 后面再说这个.)  Bob的电话看到了所有的编码, 然后选择了第一个匹配的, 在这个案例中是PCMU. 此时两个leg都协商完毕:

A leg使用G722
B leg 使用PCMU

只要Bob的电话返回一个回铃信号, 两个leg就桥接到一起了. FreeSWITCH需要在G722和PCMU之间进行转码. 平常这没啥问题, 你更希望两个电话使用相同的编码, 这样会降低CPU的使用. 这就是为什么 延后协商 要出现.

正如你能看到的, 当FreeSWITCH不知道B leg的编码时设置A leg的编码, 那么就容易让两端不匹配.  编码不匹配并不总是个坏事, 但很多情况下两个leg使用相同的编码是比较好的, 省资源嘛. 使用 延后协商和一个叫”继承编码”的技术, 我们可以强制A leg使用和B leg一样的编码.  (这个实现过程会在其他的细节中讲述, 在这里,我们先只说说这个基本的概念).

在我们的第二个呼叫流程示例中, FreeSWITCH得到了A leg后并不立即协商出个编码.  取而代之的是, FreeSWITCH在A leg执行完拨号方案前并不会进行协商编码.  在这个示例中, 拨号方案最后会bridge到bob的电话. 在bridge的过程中, FreeSWITCH和bob的电话协商出了一个编码—在这个示例中是PCMU. 一旦在B leg上协商出了编码, FreeSWITCH 返回给Alice的电话, 通知它应该使用PCMU编码. 现在, 两个leg中都使用相同的编码, 这正是我们想要的.

这就是 早期协商和延后协商的不同. 只需要记住这一点: 仅仅开启 延后协商 并不会自动强制A leg 继承 B leg 的编码.  开启延后协商仅是允许使用 “继承编码” 作为编码协商的一种方法. 延后协商还允许其他的一些编码协商的策略. 继续阅读下文了解FreeSWITCH中编码协商的更具体介绍.

 

Early Negotiation parameters
早期协商的参数

disable-transcoding      禁止转码

这个参数你可以会设置到外呼的SIP Profile中.这个参数会强制发送到B leg的编码和与A协商出来的编码一致(送单一编码, A协商成啥就送啥).

开启禁止转码, 添加以下行到你想设置的SIP profile中:

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

注意: 常见的一个误解是, 这个参数会禁用FS的转码功能.  这是错误滴.

这个参数仅仅是转换外呼的编码和呼入的编码相同, 所以就不需要转码了.

相同的效果也可以通过设置  absolute_codec_string 变量为呼入的编码来实现.

这个参数(当延后协商关闭时才生效)会获取A leg协商出来的编码, 然后把它作为和B leg协商的唯一编码(忽略任务其他的编码)如果B leg 不能接受这个编码, 通话结束.

absolute_codec_string

这个是一个通道变量, 你可以在拨号方案中进行设置, 要在bridge之前设置.
这个参数可以设置发送到B leg时的编码列表, 不考虑别的.

以下是个例子:

<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"/>

请确保使用单引号来括住逗号(‘PCMA, PCMU’) 防止它把编码列表分割成变量.

注意: 这个参数可以无视disable-transcoding参数. 所以如果你在外呼的SIP Profile中关闭了转码, 你可以在拨号方案中使用absolute_codec_string 设置让外呼的编码和主叫呼入的编码不相同,转码一样会发生.

codec_string

它是一个通道变量, 你可以在拨号方案中使用, 在Bridge前设置.
使用这个变量设置的编码会覆盖你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"/>


codec_string 和 absolute_codec_string 的区别
codec_string是覆盖Profile中的outbound-codec-prefs参数的, 所以如果设置 disable-transcoding 为true, 则codec_string就无效果了, 但absolute_codec_string无视disable-transcoding参数.

Early Negotiation + Disable Transcoding:

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

如果SOFIA PROFLE中参数disable-transcoding设置为true, 呼叫到B leg的INVITE消息将会只包含A LEG协商出来的编码.

A leg中的变量”codec_string”控制呼叫B Leg时携带哪些编码.

变量”absolute_codec_string”功能也类似, 但它实现了一个编码的隐式列表. 同时它会从编码列表中禁用其他行为中添加到A leg的编码, 相当于它是绝对的, 只要在Bridge前,设置它, 其他的都得失效.

 

Late Negotiation

延后协商(需要配置参数开启)

<param name="inbound-late-negotiation" value="true"/>

1.  进入拨号方案前不会进行编码匹配
2.  当A leg接通时协商才发生
3.  延后协商会允许你将通话路由到一个脚本, 然后检查SDP和使用通道变量”codec_string”重写可接受的编码

inherit_codec

<action application="set" data="inherit_codec=true"/>

inherit_codec=true (延后协商开启后才适用) 时当B leg 接通时才会进行编码协商, 同时会将B leg 协商出来的编码传到 A leg, 所以A 和 B就会使用相同的编码(如果A leg支持协商出来的编码); 否则, 它将会使用任何A leg可以支持的编码.

ep_codec_string

只有在SIP Profile中开启了延后协商才有效果. 这个变量是一个可读的字符串, 它包含了主叫终端提供的编码. 这个可以很容易的在拨号方案中解析.

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

Proxy Media

如果proxy media 开启和answer应用不执行,编码协商不能进行.

Warnings

如果你的SDP包含携带不同ptime设置的编码, 同时SDP中携带了ptime参数, 那么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.


Examples

以下的拨号方案示例可以允许你在使用proxy_media模式时允许你改变编码的优先顺序同时享受到proxy_media模式带来的低CPU使用. 这个有点像openser/opensips/kamailio 模块textops中的substr().

<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>

Rewriting SDP重写SDP

switch_r_sdp:

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



Disable G.729b on outbound

外呼时禁用G729B编码

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



Codec Negotiation when proxy media enabled

Proxy Media 开启后的编码协商

Correct正确

<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>


Not correct 不正确

<extension name="test_proxy_media">
  <condition field="source" expression="mod_sofia">
    <action application="playback" data="ivr/ivr-welcome_to_freeswitch.wav"/>
  </condition>
</extension>


原文地址:   
http://wiki.freeswitch.org/wiki/Codec_negotiation

另见:

 

评论已关闭。