基于Freeswitch的语音视频通话

之前写过一篇《阿里云 opensips nat内网穿透》,当时是为了解决对讲机视频对讲的问题。但是之前的方案存在一个问题,那就是虽然服务器能够正常提供服务。但是在接通之后如果设备不在同一个局域网内就会导致有音频但是没有视频信息。这个问题困扰了很久,直到现在算是能够解决这个问题。出现上面这个问题的根本原因在于设备的网络层次关系太过复杂,视频信息没有办法透传。我不是语音视频方面的专家,集中nat结构我也不在叙述了,感兴趣的访问这个链接:https://www.cnblogs.com/zhumengke/articles/11204924.html

如果要在阿里云的服务器上,还需要升级绑定弹性网卡的相关内容,具体参考这里:https://developer.aliyun.com/article/228753?spm=a2c6h.13813017.content3.1.5cbc6532wDqNf4

要实现视频信息穿透,可以采用ice方式,对应的服务端可以使用stunman(https://github.com/jselbie/stunserver).

Features:
Compliant with the latest RFCs including 5389, 5769, and 5780. Also includes
backwards compatibility for RFC 3489.
Supports both UDP and TCP on both IPv4 and IPv6.
Client test app provided.
Stun server can operate in “full” mode as well as “basic” mode. Basic mode
configures the server to listen on one port and respond to STUN binding
requests. Full mode configures the service to listen on two different IP
address interfaces (if available) and provide NAT behavior and filtering
detection support for clients.
Support for running a full mode STUN service on an Amazon EC2 instance. Run
“stunserver –help” for visit www.stunprotocol.org on how to configure this
mode.
Open source Apache license. See LICENSE file fore more details.
———————————————————

可以通过源码编译的方式进行安装:

Debian/Ubuntu/Mint
     sudo apt-get install g++
     sudo apt-get install make
     sudo apt-get install libboost-dev # For Boost
     sudo apt-get install libssl-dev # For OpenSSL

安装之后通过下面的命令启动:

./stunserver --mode full --primaryinterface 47.*.*.4 --altinterface  172.18.99.29  --verbosity 3

Freeswitch可以通过docker安装,或者源码编译:

1.docker地址:https://hub.docker.com/r/safarov/freeswitch

2.源码安装网上的安装文章也比较多了,可以搜索即可。

安装完成之后修改vars.xml文件:

vim /usr/local/freeswitch/conf/vars.xml 
<X-PRE-PROCESS cmd="set" data="global_codec_prefs=OPUS,G722,PCMU,PCMA,H263,H264,VP8"/>
 <X-PRE-PROCESS cmd="set" data="outbound_codec_prefs=OPUS,G722,PCMU,PCMA,H263,H264,VP8"/>

 <!--
     xmpp_client_profile and xmpp_server_profile
     xmpp_client_profile can be any string.
     xmpp_server_profile is appended to "dingaling_" to form the database name
     containing the "subscriptions" table.
     used by: dingaling.conf.xml enum.conf.xml
 -->

 <X-PRE-PROCESS cmd="set" data="xmpp_client_profile=xmppc"/>
 <X-PRE-PROCESS cmd="set" data="xmpp_server_profile=xmpps"/>
 <!--
      THIS IS ONLY USED FOR DINGALING

      bind_server_ip

      Can be an ip address, a dns name, or "auto".
      This determines an ip address available on this host to bind.
      If you are separating RTP and SIP traffic, you will want to have
      use different addresses where this variable appears.
      Used by: dingaling.conf.xml
 -->
 <X-PRE-PROCESS cmd="set" data="bind_server_ip=auto"/>

 <!-- NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE

      If you're going to load test FreeSWITCH please input real IP addresses
      for external_rtp_ip and external_sip_ip
 -->

 <!-- external_rtp_ip
      Can be an one of:
          ip address: "12.34.56.78"
          a stun server lookup: "stun:stun.server.com"
          a DNS name: "host:host.server.com"
      where fs.mydomain.com is a DNS A record-useful when fs is on
      a dynamic IP address, and uses a dynamic DNS updater.
      If unspecified, the bind_server_ip value is used.
      Used by: sofia.conf.xml dingaling.conf.xml
 -->
 <X-PRE-PROCESS cmd="stun-set" data="external_rtp_ip=47.*.*.4"/>

 <!-- external_sip_ip
     Used as the public IP address for SDP.
      Can be an one of:
          ip address: "12.34.56.78"
          a stun server lookup: "stun:stun.server.com"
          a DNS name: "host:host.server.com"
      where fs.mydomain.com is a DNS A record-useful when fs is on
      a dynamic IP address, and uses a dynamic DNS updater.
      If unspecified, the bind_server_ip value is used.
      Used by: sofia.conf.xml dingaling.conf.xml
 -->
 <X-PRE-PROCESS cmd="stun-set" data="external_sip_ip=47.*.*.4"/>

修改external_sip_ip external_rtp_ip 为服务器公网ip,修改global_codec_prefs outbound_codec_prefs添加H263,H264,VP8,重新启动服务即可。

在客户端中开启stun服务器:

此时再通过linphone拨打视频电话就可以看到视频了:

并且可以看到Ice status 为In progress。这就说明ice服务生效了。

添加用户可以通过下面的命令:

perl scripts/perl/add_user 1021

添加之后通过raloadxml使用户信息生效:

./fs_cli -x reloadxml

添加账号之后会发现超过1100之后的号码都无法进行呼叫,需要修改拨号计划:

root@opensips:/usr/local/freeswitch/conf/dialplan# vim default.xml

删除以下内容:

 <!--
        Snom button demo, call 9000 to make button 2 mapped to transfer the current call to a conference
    -->

    <extension name="snom-demo-2">
      <condition field="destination_number" expression="^9001$">
        <action application="eval" data="${snom_bind_key(2 off DND ${sip_from_user} ${sip_from_host} ${sofia_profile_name} message notused)}"/>
        <action application="transfer" data="3000"/>
      </condition>
    </extension>

    <extension name="snom-demo-1">
      <condition field="destination_number" expression="^9000$">
        <!--<key> <light> <label> <user> <host> <profile> <action_name> <action>-->
        <action application="eval" data="${snom_bind_key(2 on DND ${sip_from_user} ${sip_from_host} ${sofia_profile_name} message api+uuid_transfer ${uuid} 9001)}"/>
        <action application="playback" data="$${hold_music}"/>
      </condition>
    </extension>

    <extension name="eavesdrop">
      <condition field="destination_number" expression="^88(\d{4})$|^\*0(.*)$">
        <action application="answer"/>
        <action application="eavesdrop" data="${hash(select/${domain_name}-spymap/$1$2)}"/>
      </condition>
    </extension>

    <extension name="eavesdrop">
      <condition field="destination_number" expression="^779$">
        <action application="answer"/>
        <action application="set" data="eavesdrop_indicate_failed=tone_stream://%(500, 0, 320)"/>
        <action application="set" data="eavesdrop_indicate_new=tone_stream://%(500, 0, 620)"/>
        <action application="set" data="eavesdrop_indicate_idle=tone_stream://%(250, 0, 920)"/>
        <action application="eavesdrop" data="all"/>
      </condition>
    </extension>

    <extension name="call_return">
      <condition field="destination_number" expression="^\*69$|^869$|^lcr$">
        <action application="transfer" data="${hash(select/${domain_name}-call_return/${caller_id_number})}"/>
      </condition>
    </extension>

    <extension name="del-group">
      <condition field="destination_number" expression="^80(\d{2})$">
        <action application="answer"/>
        <action application="group" data="delete:$1@${domain_name}:${sofia_contact(${sip_from_user}@${domain_name})}"/>
        <action application="gentones" data="%(1000, 0, 320)"/>
      </condition>
    </extension>
<extension name="add-group">
      <condition field="destination_number" expression="^81(\d{2})$">
        <action application="answer"/>
        <action application="group" data="insert:$1@${domain_name}:${sofia_contact(${sip_from_user}@${domain_name})}"/>
        <action application="gentones" data="%(1000, 0, 640)"/>
      </condition>
    </extension>

    <extension name="call-group-simo">
      <condition field="destination_number" expression="^82(\d{2})$">
        <action application="bridge" data="{leg_timeout=15,ignore_early_media=true}${group(call:$1@${domain_name})}"/>
      </condition>
    </extension>

    <extension name="call-group-order">
      <condition field="destination_number" expression="^83(\d{2})$">
        <action application="bridge" data="{leg_timeout=15,ignore_early_media=true}${group(call:$1@${domain_name}:order)}"/>
      </condition>
    </extension>

    <extension name="extension-intercom">
      <condition field="destination_number" expression="^8(10[01][0-9])$">
        <action application="set" data="dialed_extension=$1"/>
        <action application="export" data="sip_auto_answer=true"/>
        <action application="bridge" data="user/${dialed_extension}@${domain_name}"/>
      </condition>
    </extension>

 

修改以下内容:

<!-- If sip_req_host is not a local domain then this has to be an external sip uri -->
   <!--
   <extension name="external_sip_uri" continue="true">
     <condition field="source" expression="mod_sofia"/>
     <condition field="${outside_call}" expression="^$"/>
     <condition field="${domain_exists(${sip_req_host})}" expression="true">
       <anti-action application="bridge" data="sofia/${use_profile}/${sip_to_uri}"/>
     </condition>
   </extension>
   -->
   <!--
       Snom button demo, call 9000 to make button 2 mapped to transfer the current call to a conference
   -->


   <!--
        dial the extension (1000-1019) for 30 seconds and go to voicemail if the
        call fails (continue_on_fail=true), otherwise hang up after a successful
        bridge (hangup_after_bridge=true)
   -->
   <extension name="Local_Extension">
     <condition field="destination_number" expression="^([1-9][0-9]*)$"> <!-- 主要修改此处正则表达式,去掉前面的各种附加条件 -->
       <action application="export" data="dialed_extension=$1"/>
       <!-- bind_meta_app can have these args <key> [a|b|ab] [a|b|o|s] <app> -->
       <action application="bind_meta_app" data="1 b s execute_extension::dx XML features"/>
       <action application="bind_meta_app" data="2 b s record_session::$${recordings_dir}/${caller_id_number}.${strftime(%Y-%m-%d-%H-%M-%S)}.wav"/>
       <action application="bind_meta_app" data="3 b s execute_extension::cf XML features"/>
       <action application="bind_meta_app" data="4 b s execute_extension::att_xfer XML features"/>
       <action application="set" data="ringback=${us-ring}"/>
       <action application="set" data="transfer_ringback=$${hold_music}"/>
       <action application="set" data="call_timeout=30"/>
       <!-- <action application="set" data="sip_exclude_contact=${network_addr}"/> -->
       <action application="set" data="hangup_after_bridge=true"/>
       <!--<action application="set" data="continue_on_fail=NORMAL_TEMPORARY_FAILURE,USER_BUSY,NO_ANSWER,TIMEOUT,NO_ROUTE_DESTINATION"/> -->
       <action application="set" data="continue_on_fail=true"/>
       <action application="hash" data="insert/${domain_name}-call_return/${dialed_extension}/${caller_id_number}"/>
       <action application="hash" data="insert/${domain_name}-last_dial_ext/${dialed_extension}/${uuid}"/>
       <action application="set" data="called_party_callgroup=${user_data(${dialed_extension}@${domain_name} var callgroup)}"/>
       <action application="hash" data="insert/${domain_name}-last_dial_ext/${called_party_callgroup}/${uuid}"/>
       <action application="hash" data="insert/${domain_name}-last_dial_ext/global/${uuid}"/>
       <!--<action application="export" data="nolocal:rtp_secure_media=${user_data(${dialed_extension}@${domain_name} var rtp_secure_media)}"/>-->
       <action application="hash" data="insert/${domain_name}-last_dial/${called_party_callgroup}/${uuid}"/>
       <action application="bridge" data="user/${dialed_extension}@${domain_name}"/>
       <action application="answer"/>
       <action application="sleep" data="1000"/>
       <action application="bridge" data="loopback/app=voicemail:default ${domain_name} ${dialed_extension}"/>
     </condition>
   </extension>

linphone安卓版本下载地址:https://www.linphone.org/releases/android/

参考链接:

https://github.com/jselbie/stunserver

https://freeswitch.org/confluence/display/FREESWITCH/NAT+Traversal

https://hub.docker.com/r/safarov/freeswitch

https://www.linphone.org/releases/android/

https://freeswitch.org/confluence/display/FREESWITCH/NAT+Traversal#NATTraversal-UsingSTUNtoaidinNATTraversal

☆版权☆

* 网站名称:obaby@mars
* 网址:https://oba.by/
* 个性:https://oba.by/
* 本文标题: 《基于Freeswitch的语音视频通话》
* 本文链接:https://lang.bi/2021/10/9153
* 短链接:https://oba.by/?p=9153
* 转载文章请标明文章来源,原文标题以及原文链接。请遵从 《署名-非商业性使用-相同方式共享 2.5 中国大陆 (CC BY-NC-SA 2.5 CN) 》许可协议。


You may also like

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注