• <tr id='y374v'><strong id='y374v'></strong><small id='y374v'></small><button id='y374v'></button><li id='y374v'><noscript id='y374v'><big id='y374v'></big><dt id='y374v'></dt></noscript></li></tr><ol id='y374v'><table id='y374v'><blockquote id='y374v'><tbody id='y374v'></tbody></blockquote></table></ol><u id='y374v'></u><kbd id='y374v'><kbd id='y374v'></kbd></kbd>
    <i id='y374v'><div id='y374v'><ins id='y374v'></ins></div></i>

    <i id='y374v'></i>

      1. <fieldset id='y374v'></fieldset>

          <acronym id='y374v'><em id='y374v'></em><td id='y374v'><div id='y374v'></div></td></acronym><address id='y374v'><big id='y374v'><big id='y374v'></big><legend id='y374v'></legend></big></address><span id='y374v'></span>

          <code id='y374v'><strong id='y374v'></strong></code>

            <ins id='y374v'></ins><dl id='y374v'></dl>

            iOS 实现Voip网络电话

            • 时间:
            • 浏览:9
            • 来源:124软件资讯网

              前言

              Voip即网络电话  ,voice over internet Protocol  ,将模拟的声音讯号经由压缩与封包之后  ,以数据封包的形式在IP网络举行语音讯号的传输  ,通俗来说就是互联网电话或IP电话 。Voip网络电话  ,中文就是“通过Ip数据包发送实现的语音营业”  ,它使你可以通过互联网免费或是资源很低的传送语音  ,传真 ,视频和数据等营业  。

              基本原理

              • Voip的基本原理是通过语音的压缩算法对语音数据编码举行压缩处置惩罚  ,然后把这些语音数据按TCP/IP尺度举行打包  ,经由IP网络把数据包送至吸收地 ,再把这些语音数据包串起来  ,经由解压处置惩罚后  ,恢复成原来的语音信号  ,从而到达由互联网传送语音的目的  。

              • Voip电话的焦点与要害装备是Voip网关  。

              • 在整个IP电话系统中  ,网关设立在天下上各个地域 ,完成当地电话网(PSTN)与Internet的接入与转换处置惩罚等功效  。终端装备将模拟语音信号传到Voip网关  ,网关吸收到了尺度电话信号以后  ,经由数据自 ,编码  ,压缩处置惩罚  ,按IP协议打包到Internet上  ,凭据传输路由  ,通过Internet发送到对应网关  ,对端的网关吸收到了Internet传来的IP包 ,经由解压处置惩罚后还原成模拟语音信号再转到电话网系统(PSTN)

              • 网关因生产厂商差别而有所差别  ,但基本的组成模块是一样的  ,包罗数据处置惩罚主机  ,语音模块 ,数据处置惩罚模块 ,数据接续模块和治理软件模块等  。

              • 网关具有路由治理功效  ,它把各地域电话区号映射为响应地域网关的IP地址  ,这些信息存放在一个数据库中  。数据接续处置惩罚软件将完成呼叫处置惩罚  ,数字语音打包  ,路由治理等功效  。在用户拨打VOIP电话时  ,网关凭据电话区号数据库资料  ,确定响应网关的IP地址  ,并将此IP地址加入IP数据包中  ,同时选择最佳路由  ,以淘汰传输延时 ,IP数据包经Internet到达目的地的网关 。

              常用的Voip协议(Control Protocol)

              H.323

              • H.323是一种ITU-T尺度  ,最初用于局域网上的多媒体集会  ,厥后扩展至笼罩Voip  。该尺度既包罗了点对点通讯也包罗了多点集会  。H.232界说了四种逻辑组成部门:终端 ,网关 ,网守以及多点控制单元  。

              • 终端  ,网关和MCU均视为终端点 。

              • 会话提倡协议(SIP) 是简历Voip链接的IETF尺度 。SIP是一种应用层控制协议  ,用于和一个或多个到场者建立  ,修改和终止会话 。SIP的结构与HTTP(客户端-服务器协议)相似  。客户机发出请求  ,并发送给服务器  ,服务器处置惩罚这些请求给客户机发送一个响应 ,该请求与响应行程一次事务  。

              • 媒体网关控制协议MGCP 界说了呼叫控制单元与电话网关之间的通讯服务  。

              • 媒体网关控制协议是IETF和IU-T配合起劲的效果 。是一种用于控制物理上离开的多媒体网关的协议单元的协议 ,从而可以从媒体转化中分散呼叫控制  。

              SIP协议

              • Voip一样平常通过SIP作为应用层的信令控制协议【session intiation protocol】, SIP通过建立会话  ,断开会话从而实现会话的治理  。SIP可以建立多个会话 ,多个会话可以同时事情 ,对多会话举行治理从而实现多会话的功效  。SIP在传输层可以接纳UDP协议也可以接纳TCP协议  ,可是对于对讲的特点来说大部门接纳的是UDP协议  ,UDP协议简朴而且越发切合对讲的即时性的特点  ,可是在网络欠好情形下会发生丢包 。

              • SIP会话建设之后RTCP和RTP划分卖力会话上媒体传输的控制和媒体传输 。RTP上传输的媒体的编码方式为会话建设时客户端和服务器之间协商的编码方式  ,RTP协媾和RTP控制协议RTCP一起使用  ,而且它是建设在用户数据协议UDP上的  。

              要害手艺

              VOIP网络电话的要害手艺包罗:信令手艺  ,编码手艺  ,实时传输手艺  ,服务质量保证手艺  ,以及网络传输手艺等

              信令手艺

              • 信令手艺保证电话呼叫的顺遂实现和语音质量  ,被普遍接受的Voip控制信令系统包罗ITUT的H.323系列和IETF的会话初始化协议SIP

              • ITU的H.232协议界说了在无营业质量保证的因特网或其他分组网上多媒体通讯的协议以及其规程  。H.323尺度是局域网  ,广域网和internet上的多媒体提供手艺基础保障

              • SIP是一种应用层协议 ,可以用UDP或TCP作为传输协议  。与H.323差别的是:SIP是一种基于文本的协议  ,用SIP规则资源定位语言形貌(SIP Uniform Resource Locators)  ,这样易于实现和调试  ,更主要的是天真性和扩展性好  。由于SIP仅作用于初始化呼叫  ,而不是传输媒体数据 ,因而造成的附加传输价格也不大 。与H.323相比  ,SIP另有建设呼叫快 ,支持传送电话号码的特点  。

              编码手艺

              • 语音压缩编码手艺是voip网络电话手艺的一个主要组成部门  。主要的编码手艺有ITU-T界说的G.729 G.723等  ,其中G.729 可将经由采样的64kbit/s 话音以险些不失真的质量压缩至8kbit/s  。由于在分组交流网络中 ,营业质量不能获得很好保证  ,因而需要话音的编码具有一定的天真性  ,即编码速率、编码尺度的可变可顺应性  。G723.1 接纳5.3/6.3K bit/s 双速率话音编码  ,其话音质量好  ,可是处置惩罚时延较大  ,它是已尺度化的最低速率的话音编码算法  。

              实时传输手艺

              • 实时传输手艺主要是接纳RTP实时传输协议  。RTP是提供端到端的包罗音频在内的实时数据传送的协议  ,RTP尺度界说了两个子协议  ,RTP和RTCP

              • 数据传输协议RTP  ,用于实时传输数据 。改协议提供的信息包罗:时间戳(用于同步) ,序列号(用于丢包和重排序检测) ,以及负载花样(用于说明数据的编码花样)

              • 控制协议RTCP  ,用于服务质量反馈和同步流媒体  。相对于RTP来说  ,RTCP所占的带宽很是小  ,通常只有5%

              RTP

              • RTP数据协议卖力对流媒体数据举行封包并实现媒体流的实时传输  ,每一个RTP数据报都由头部Header和负载Payload两个部门组成  ,其中头部前12个字节的寄义是牢固的  ,而负载则可以是音频或视频数据

              • RTP头部花样

              V:RTP协议的版本号  , 占2位  ,当前协议版本号为2

              P:填充标志  , 占1位  ,若是P = 1 , 则在该报文的尾部填充一个或多个分外的八位组  ,它们不是有用载荷的一部门

              X:扩展标志  ,占1位 ,若是X =1  , 则在RTP报头后跟有一个扩展报头

              CC:CSRC计数器 ,占4位  ,指示CSRC表现符的个数

              M:标志  ,占1位  ,差别的有用载荷有差别的寄义 ,对于食物 ,标志一帧的竣事 ,对于音频 ,标志会话的最先

              PT:有用荷载类型 ,占7位  , 用于说明RTP报文中有用载荷的类型 ,如GSM音频 ,JPEM图像等 ,在流媒体中大部门是用来区分音频流和视频流的  ,这样便于客户端举行剖析 。

              序列号:占16位 ,用于标识发送者所发送的RTP报文的序列号  ,每次发送一个报文 ,序列号加1 。 这个字段当下层的承载协议用UDP的时间  ,网络状态欠好的时间可以用来检查丢包  ,同时泛起网络发抖的情形可以用来对数据举行重新排序  ,序列号的初始值是随机的  ,同时音频包和视频包的sequence是划分记数的  。

              时戳(Timestamp):占32位  ,必须使用90kHZ时钟频率  。时戳反映了该RTP报文的第一个八位组的采样时刻  。接受者使用时戳来极端延迟和延迟发抖  ,并举行同步控制  。

              同步信源(SSRC)标识符:占32位  ,用于表现同步信源  。该标识符是随机选择的  ,到场统一视频集会的两个同步信源不能有相同的SSRC

              特约信源(CSRC)标识符:每个CSRC标识符占32位  ,可以有0-15个  ,每个CSRC标识了包罗在该RTP报文有用载荷中的所有特约信源

              网络传输手艺

              • Voip网络电话中网络传输手艺主要是TCP和UDP  ,此外还包罗网关互联手艺  ,路由选择手艺  ,网络治理手艺以及宁静认证和计费手艺等

              服务质量手艺

              Voip网络电话中主要接纳资源预留协议RSVP 以及举行服务质量监控的实时传输控制协议RTCP来制止网络拥塞 ,保障通话质量

              小型Voip电话系统的建设

              一样平常而言  ,部署Voip系统时涉及三个主要组成部门:IPPBX  ,VOIP电话和Voip运营商网络  。我们总是通过Voip网络和传统的PSTN电话系统举行互联  。固然  ,我们也可以通过部署Voip网关(VoIP Gateway)直接毗连传统电话网络  ,而不需要单独的voip服务商  。

              本文主要接纳miniSIPService搭建Voip服务系统  。MSS是专业的纯软件方式的PBX ,能运行在Windows以及Linux等系统 。它能支持我们现在需要的种种特征  。

              在iOS上开发Voip应用

              现在基于SIP协议的Voip是应用最普遍的  ,iOS上的Voip应用通常使用开源协议栈举行开发  ,成熟的开源SIP协议栈有PJSIP和Linphone,本文将通过PJSIP来实现一个简朴的语音通话App

              PJSIP开源库的组成部门

              PJSIP -Open Source SIP stack[开源的SIP协议栈]

              PJMEDIA - Open Source Media Stack[开源的媒体栈]

              PJNATH - Open Source NAT Traversal Helper Library[开源的NAT-T辅助库]

              PJLIB-UTIL - Auxiliary Library[辅助工具库]

              PJLIB - Ultra Portable Base Framework Library[基础框架库]

              PJSUA API使用以及说明

              这里使用的API基本都是来自pjsua  ,这个是建设在pjsip基础上的一层纯C封装  ,下面展示了pjsip初始化到拨打电话和挂断电话的Api挪用逻辑

              pjsua接口使用时  ,需要建立  ,初始化  ,最先和销毁的操作:pjsua_create,pjsua_init,pjsua_start,pjsua_destory

              pjsua_transport_create建立sip信令发送和接受需要的相关socket等资源

              pjsua_acc_add添加拨打电话账号  ,账号类似于我们的手机号码  ,可以起到定位的功效  。

              拨打电话的挂断电话:pjsua_call_make_call,pjsua_call_hangup_all

              Pjsip只是完成两个功效

              1.使用sip信令协商双方使用音频 ,视频通话使用的rtp  ,rtcp的socket端口  ,视频编码器和音频编码器的类型和相关的编码参数  ,使用的网络类型  。

              2.完成音频  ,视频通话的socket通道  ,传出音频和视频数据

              服务器的安装

              1.安装MySQL

              去官方下载最新版本的MySQL  ,本文使用最新的MySQL Community Server 5.6.25 GA版  ,Mac用户利便起见可以使用dmg花样的  。由于OS X的路径问题  ,后续启动Kamailio时会发生找不到库的情形  ,以是需要建立库的软链接  ,即安装完毕后终端执行下令:

              # sudo ln -s/usr/local/mysql/lib/libmysqlclient.18.dylib /usr/lib/libmysqlclient.18.dylib

              最后我们就可以在系统偏好设置里将MySQL Server打开了 。

              2.源码下载

                # git clone --branch 4.2 --single-branch \
                
                   git://git.sip-router.org/kamailiokamailio
                   
                # cd kamailio

               我是直接凭据链接下载的 ,下载之后直接解压  ,链接地址

              3.编译安装

              在执行完步骤2的基础上  ,首先添加MySQL的情况变量:

              # export PATH=$PATH:/usr/local/mysql-5.6.25-osx10.8-x86_64/bin

              然后执行下列下令举行编译与安装

              # make include_modules=db_mysql cfg
              
              # sudo make all
              
              # sudo make install

              make all的时间若是不使用sudo提权的话  ,可能就失败了  。虽然sudo不是好的做法  ,可是利便起见这里直接sudo 。这边下去会泛起的一个问题是mysql的module编译失败  。提醒“mysql/mysql.h”找不到 。这时间要去kamailio-4.3.0/modules/db_mysql下  ,把源码头文件引用都改成  。

              这时间你应该可以完成make all了  。接下去install  ,这一步也不会出问题了  。

              4.Kamailio的设置

              修改kamctlrc文本 ,执行

              # sudo vim /usr/local/etc/kamailio/kamctlrc

              去掉SIP_DOMAIN前的#符号  ,改成自己的服务器地址  。

              我的是SIP_DOMAIN=127.0.0.1 。即本机IP  。

              然后去掉 DBENGINE=MYSQL前的注释语句 ,选定mysql数据库 。

              然后再修改kamailio.cfg文本  ,执行

              # sudo vim /usr/local/etc/kamailio/kamailio.cfg

              在文本的开头加上一行:

              #!defineWITH_MYSQL

              5.建立数据库 ,并开启服务器

              执行下令来建立数据库

              # /usr/local/sbin/kamdbctl create

              若是提醒输入密码  ,此时注重密码是默认在kamdbctl的设置文件中的  ,通过下面下令可以检察

              cd /usr/local/etc/kamailio/
              vim kamctlrc

              你可以看到服务器的一些设置 ,若是密码自己设置错误  ,或者不知  ,则可以将下面几行打开并举行重新执行数据库建立的下令 。

              下面报错:

              /usr/local/lib/kamailio/kamctl/kamdbctl.mysql: line 105: mysql: 
              > command not found

              解决的措施:

              # ln -s /usr/local/mysql/bin/mysql /usr/bin

              建立完数据库后需要添加用户

              我们这里添加三个用户user1, user2以及user3  ,密码都是123:

              cuilinhaodeMacBook-Pro:sbin cuilinhao$ ./kamctl add user1 123
              mysql: [Warning] Using a password on the command line interface can be insecure.
              mysql: [Warning] Using a password on the command line interface can be insecure.
              new user 'user1' added
              cuilinhaodeMacBook-Pro:sbin cuilinhao$ ./kamctl add user2 123
              mysql: [Warning] Using a password on the command line interface can be insecure.
              mysql: [Warning] Using a password on the command line interface can be insecure.
              new user 'user2' added

              使用工具检察  ,如下图

              添加完用户后需要开启SIP Server:

              cuilinhaodeMacBook-Pro:sbin cuilinhao$ ./kamctl start
              
              \E[37;33mINFO: Starting Kamailio :

              若是这一步出问题注重检查步骤1中的库的软链接是否建设  。除了软毗连的问题  ,实在更主要的一个问题是会提醒

              \E[37;31mERROR: PID file /var/run/kamailio/kamailio.pid does not exist -- Kamailio start failed
              cuilinhaodeMacBook-Pro:sbin cuilinhao$ vim /var/run/kamailio/kamailio.pid
              cuilinhaodeMacBook-Pro:sbin cuilinhao$ cd /var/run/

              这里有一个权限问题  ,而并非是某个模块没有编译  。使用

              kamailio -M 8 -E -e -dd

              可以检察详细失败的信息  ,也可以去系统日志看  。详细的错误缘故原由是ERROR: init_unix_sock: bind: No such file or directory 。一个简朴的解决措施是sudo vim /usr/local/etc/kamailio/kamctlrc ,去掉 PID_FILE=/var/run/kamailio/kamailio.pid前的#号  ,然后在/var/run下新建一个归属于当前Mac用户的kamailio文件夹  。

              uilinhaodeMacBook-Pro:sbin cuilinhao$ ls -lh /var/run/

              查找kamalio,

              cuilinhaodeMacBook-Pro:sbin cuilinhao$ ls -lh /var/run/ |grep kam
              drwxr-xr-x  6 root             daemon            192B 12 21 17:43 kamailio
              cuilinhaodeMacBook-Pro:sbin cuilinhao$ ls -lh /var/run/kamailio/
              total 8
              -rw-r--r--  1 root  daemon     5B 12 21 17:43 kamailio.pid
              srw-------  1 root  daemon     0B 12 21 17:43 kamailio_ctl
              prw-rw----  1 root  daemon     0B 12 21 17:43 kamailio_rpc.fifo
              srw-rw----  1 root  daemon     0B 12 21 17:43 kamailio_rpc.sock

              重置一下kamalio的权限品级

              cuilinhaodeMacBook-Pro:sbin cuilinhao$ whoami
              cuilinhao
              cuilinhaodeMacBook-Pro:sbin cuilinhao$ chow -R cuilinhao:staff /var/run/kamailio/
              -bash: chow: command not found
              cuilinhaodeMacBook-Pro:sbin cuilinhao$ chown -R cuilinhao:staff /var/run/kamailio/
              chown: /var/run/kamailio//kamailio_ctl: Operation not permitted
              chown: /var/run/kamailio//kamailio_rpc.fifo: Operation not permitted
              chown: /var/run/kamailio//kamailio_rpc.sock: Operation not permitted
              chown: /var/run/kamailio//kamailio.pid: Operation not permitted
              chown: /var/run/kamailio/: Operation not permitted
              cuilinhaodeMacBook-Pro:sbin cuilinhao$ sudo chown -R cuilinhao:staff /var/run/kamailio/
              
              chown: /var/run/kamailio//kamailio_ctl: Operation not permitted
              chown: /var/run/kamailio//kamailio_rpc.fifo: Operation not permitted
              chown: /var/run/kamailio//kamailio_rpc.sock: Operation not permitted
              chown: /var/run/kamailio//kamailio.pid: Operation not permitted
              chown: /var/run/kamailio/: Operation not permitted
              cuilinhaodeMacBook-Pro:sbin cuilinhao$ sudo chown -R cuilinhao:staff /var/run/kamailio/
              cuilinhaodeMacBook-Pro:sbin cuilinhao$ ls -lh /var/run/ |grep kam
              drwxr-xr-x  6 cuilinhao        staff             192B 12 21 17:43 kamailio
              cuilinhaodeMacBook-Pro:sbin cuilinhao$ ./kamctl stop
              
              \E[37;33mINFO: Stopping Kamailio :
              ./kamctl: line 2013: kill: (1728) - Operation not permitted
              \E[37;33mINFO: stopped

              再次开启如下:

              cuilinhaodeMacBook-Pro:sbin cuilinhao$ ./kamctl start
              
              \E[37;33mINFO: Starting Kamailio :
               1728   ??  S      0:00.02 ./kamailio -P /var/run/kamailio/kamailio.pid -f /usr/local/etc/kamailio//kamailio.cfg
               1729   ??  S      0:00.00 ./kamailio -P /var/run/kamailio/kamailio.pid -f /usr/local/etc/kamailio//kamailio.cfg
               1730   ??  S      0:00.00 ./kamailio -P /var/run/kamailio/kamailio.pid -f /usr/local/etc/kamailio//kamailio.cfg

              则服务器开启乐成  。

              现在自己电脑启动服务器

                cd /usr/local/sbin/
                ls
              cuilinhaodeMacBook-Pro:sbin cuilinhao$ sudo ./kamctl start
              
              \E[37;33mINFO: Starting Kamailio :
              \E[37;33mINFO: started (pid: 1926)
              cuilinhaodeMacBook-Pro:sbin cuilinhao$

              自己电脑mySql 密码 root

              Kamailio 密码 Kamailiorw

              Demo演示 焦点代码

              初始化

              初始化pjsip,该要领在APPDelegate中挪用  ,主要是对通话举行设置  ,来电回调设置以及媒体先关的设置

              代码如下:

              ///初始化通话设置
                      pjsua_config config;
                      pjsua_config_default (&config);
                      //登录状态改变回调
                      //config.cb.on_reg_state2 = &on_reg_state2;
                      config.cb.on_reg_state = &on_reg_state;
                      
                      
                      //来电回调
                      config.cb.on_incoming_call = &on_incoming_call;
                      //媒体状态回调(通话建设后 ,要播放RTP流)
                      config.cb.on_call_media_state = &on_call_media_state;
                      //设置通话状态改变回调
                      config.cb.on_call_state = &on_call_state;
                      
                      //媒体相关设置
                      pjsua_media_config media_config;
                      pjsua_media_config_default(&media_config);
                      media_config.clock_rate = 16000;
                      media_config.snd_clock_rate = 16000;
                      media_config.ec_tail_len = 0;
                      
                      
                      //初始化日志设置
                      pjsua_logging_config log_config;
                      pjsua_logging_config_default(&log_config);
                      
                      //日志品级0不打印日志 4打印详情日志
                      //log_config.console_level = 0;
                      //status = pjsua_init(&config, &log_config, NULL);
                      //判断是否初始化乐成
                      //if (status != PJ_SUCCESS)
                      //{
                        //  NSLog(@"创初始化pjsua设置失败");
                      //}
                      
                      log_config.msg_logging = PJ_TRUE;
                      log_config.console_level = 4;
                      log_config.level = 5;
                      
                      //初始化pjsua设置
                      status = pjsua_init(&config, &log_config, &media_config);
                      //判断是否初始化乐成
                      if (status != PJ_SUCCESS)
                      {
                          NSLog(@"---初始化pjsua设置失败----");
                      }
                      
                      //初始化UDP
                      {
                          pjsua_transport_config config;
                          pjsua_transport_config_default(&config);
                          
                          status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &config, NULL);
                          if (status != PJ_SUCCESS)
                          {
                              NSLog(@"--添加UDP传输失败---");
                          }
                      }

              设置乐成之后可以看到下面打印效果  。

              回调函数设置

              ///登录状态改变回调
              static void on_reg_state2(pjsua_acc_id acc_id, pjsua_reg_info *info)
              {
                 
                  
                  if (info->renew != 0) {
                      //--info内里是一个结构体 cbparm也是一个结构体  , 然后cbparm中包罗code属性  ,这是一个c语言的写法
                      if (info->cbparam->code == 200) {
                          NSLog(@"登录乐成");
                      }
                      else{
                          NSLog(@"登录失败code:%d ",info->cbparam->code);
                      }
                  }
                  else{
                      if (info->cbparam->code == 200)
                      {
                          NSLog(@"SIP退出登录乐成");
                      }
                  }
              }
              ///来电回调
              static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata){
                  //获取来电信息
                  pjsua_call_info info;
                  pjsua_call_get_info(call_id, &info);
                  NSString *callStr = [NSString stringWithUTF8String:info.remote_info.ptr];
                  //这里发送一个通知
                  [[NSNotificationCenter defaultCenter] postNotificationName:@"calling" object:nil userInfo:@{@"calledCAcount":callStr}];
                  NSLog(@"%@",callStr);
              }
              ///呼叫回调
              static void on_call_media_state(pjsua_call_id call_id)
              {
                  //获取呼叫信息
                  pjsua_call_info info;
                  pjsua_call_get_info(call_id, &info);
                  
                  if (info.media_status == PJSUA_CALL_MEDIA_ACTIVE)
                  {//呼叫接通
                      
                      //建设单向媒体流从源到汇
                      pjsua_conf_connect(info.conf_slot, 0);
                      pjsua_conf_connect(0, info.conf_slot);
                      
                      NSLog(@"呼叫乐成,等候对方接听");
                  }
              }
              //通话状态改变回调
              static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
              {
                  
                  // 通话状态:CALLING
                  // 通话状态:EARLY
                  // 通话状态:EARLY
                  // 呼叫乐成,等候对方接听
                  // 通话状态:CONNECTING
                  // 通话状态:CONFIRMED
                  // DISCONNCTD  对方挂断
                  //获取通话信息
                  pjsua_call_info ci;
                  pjsua_call_get_info(call_id, &ci);
                  
                  NSString *status = [NSString stringWithUTF8String:ci.state_text.ptr];
                  NSLog(@"通话状态:%@",status);
                  
              }

              登录界面实现

              首先要监听注册相关通知  ,在SIP中  ,注册就是将客户端的相关信息注册到服务器的一个类似路由表一样的列表中  ,这样其他客户端呼叫时  ,经由服务器的路由就可以找到准确的客户端地址 ,从而建设P2P的链接  。

              - (void)viewDidLoad {
                  [super viewDidLoad];
                  
                  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleRegisterStatus:) name:@"SIPRegisterStatusNotification" object:nil];
                  
              }

              登录按钮触摸事务中  ,设置并完成对服务器的注册:

              //设置账号信息
                  pjsua_acc_config config;
                  pjsua_acc_config_default(&config);
                  
                  char accountChar[50];
                  strcpy(accountChar, [accountsString UTF8String]);
                  char passwordChar[50];
                  strcpy(passwordChar, [passwordString UTF8String]);
                  
                  //设置账号花样:  sip:账号@服务地址
                  char sipAccount[50];
                  sprintf(sipAccount, "sip:%s@%s", accountChar, [ipString UTF8String]);
                  config.id = pj_str(sipAccount);
                  
                  //设置服务器花样: sip:服务器地址
                  char serviceId[50];
                  sprintf(serviceId, "sip:%s",[ipString UTF8String]);
                  
                  NSLog(@"----服务器地址---%s", [ipString UTF8String]);
                  
                  config.reg_uri = pj_str(serviceId);
                  
                  //不设置超时时间
                  config.reg_retry_interval = 0;
                  
                  //注册账号个数 最多8个
                  config.cred_count = 1;
                  //注册方案
                  config.cred_info[0].scheme = pj_str("Digest");
                  //符号“*”
                  config.cred_info[0].realm = pj_str("*");
                  //账号
                  config.cred_info[0].username = pj_str(accountChar);
                  //数据类型
                  config.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
                  //密码
                  config.cred_info[0].data = pj_str(passwordChar);
                  status = pjsua_acc_add(&config, PJ_TRUE, &_acc_id);
                  if (status != PJ_SUCCESS)
                  {
                      NSLog(@"---登录SIP电话失败");
                      return NO;
                  }
              • 在设置中config.reg_retry_interval应设置为0  ,这个是注册失败时  ,举行重试的时间距离 ,若是不设置为0则它会不停举行实验注册(用户名密码验证失败也会云云)  ,这里为了不做庞大的类似超市这样的处置惩罚 ,直接将它设为0

              • 若是登录乐成之后就会举行回调

              pjsip_status_code status = [notification.userInfo[@"status"] intValue];
                  NSString *statusText = notification.userInfo[@"status_text"];
                  
                  
                  //---
                  //账号ID
                  //pjsua_acc_id acc_id = [notification.userInfo[@"acc_id"] intValue];
                  
                  if (status != PJSIP_SC_OK)
                  {
                      NSLog(@"--------登录失败,错误信息: %d(%@)", status, statusText);
                      return;
                  }
                  
                  NSLog(@"----->>>登录回调乐成");
                  
                  UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
                  UIViewController *dialViewController = [sb instantiateViewControllerWithIdentifier:@"DialVC"];
                  
                  
                  CATransition *transition = [[CATransition alloc] init];
                  
                  transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
                  transition.type = kCATransitionFade;
                  transition.duration  = 0.5;
                  transition.removedOnCompletion = YES;
                  
                  UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
                  [keyWindow.layer addAnimation:transition forKey:@"change_view_controller"];
                  
                  keyWindow.rootViewController = dialViewController;

              通过通知带回来的信息  ,来判断是否登录乐成  ,若是登录乐成则直接跳转到拨打电话界面  。

              拨打电话实现

              主要是挪用pjsua_call_make_call要领 ,凭据用户名举行呼叫  ,呼叫分一下几个状态:

              • PJSIP_INV_STATE_DISCONNECTED  呼叫状态

              • PJSIP_INV_STATE_CALLING 呼叫中 状态

              • PJSIP_INV_STATE_CONNECTING 正在毗连状态

              • PJSIP_INV_STATE_CONFIRMED  挂断状态

              char accountChar[50];
                  sprintf(accountChar,"sip:%s@%s",[accountsString UTF8String],[self.ip UTF8String]);
                  pj_str_t url = pj_str(accountChar);
                  
                  //初始化呼叫
                  pjsua_call_setting  call_set;
                  pjsua_call_setting_default(&call_set);
                  
                  pj_status_t status = pjsua_call_make_call(_acc_id, &url, &call_set, NULL, NULL, NULL);
                  if (status != PJ_SUCCESS)
                  {
                      NSLog(@"呼叫失败");
                  }

              接听功效实现

              首先要添加监听通知

              - (void)viewDidLoad {
                  [super viewDidLoad];
                  
                  _phoneLab.text = self.phoneNum;
                  
                  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleCallStatuschange:) name:@"SIPCallStatusChangedNotification" object:nil];
              }

              接电话直接挪用pjsua_call_answer要领  ,改要领中需要将接听的ID直接传进去就可以举行通话

              - (void)answerCall:(pjsua_call_id)callId
              {
                  pjsua_call_answer(callId, 200, NULL, NULL);
              }

              若是挂断  ,可以直接挪用pjsua_call_hangup_all()要领就可以了  。

                  //获账户信息
                  pjsua_call_info config;
                  pjsua_call_get_info(_acc_id, &config);
                  
                  pjsua_call_hangup_all();

              Demo:https://github.com/cuilinhao/Voip_Project.git