PJSIP
pjsip编译指南
pjsip编译指南
pjsip是一个开源的、基于C语言的VoIP库,用于构建高性能的SIP和多媒体通信应用程序。
以下是pjsip的编译指南:
1. 准备环境:确保您的系统上安装了所需的依赖项和工具,包括编译器、make工具、Git等。
2. 获取源代码:通过Git或其他方式获取pjsip的源代码。
3. 配置项目:运行`configure`脚本,该脚本将检查您的系统上是否满足编译要求,并设置必要的编译选项。
您可以通过传递参数来自定义配置,例如指定安装目录、启用或禁用某些功能等。
4. 编译项目:运行`make`命令来编译项目。
这将编译pjsip库以及示例应用程序。
5. 安装:运行`make install`命令来将编译的库和头文件安装到指定的目录中。
6. 测试:运行示例应用程序以测试pjsip库的功能。
需要注意的是,pjsip的编译过程可能会因系统和环境的不同而有所不同。
如果您在编译过程中遇到问题,建议查阅pjsip的文档或寻求社区支持。
pjsip用例
pjsip用例Pjsip是一种开源的多媒体通信库,它提供了一套用于实现音频、视频和即时通信功能的API。
本文将介绍pjsip的几个常见用例,以帮助读者更好地理解和使用该库。
一、实现即时通信功能即时通信是pjsip最常见的用例之一。
通过pjsip,我们可以轻松地实现基于SIP协议的语音和视频通话。
首先,我们需要创建一个SIP账号,并与服务器进行注册。
然后,我们可以使用pjsip提供的API来拨打和接听电话,并进行音频和视频的传输。
此外,pjsip 还支持实时的文字聊天功能,使得我们可以与对方进行文本交流。
二、实现音频和视频通话除了即时通信,pjsip还可以用于实现音频和视频通话。
我们可以使用pjsip提供的API来创建一个音频或视频通话会话,并进行音频和视频的传输。
在音频通话中,pjsip支持各种音频编解码器,包括G.711、G.722、G.729等。
在视频通话中,pjsip支持各种视频编解码器,如H.264、VP8等。
通过pjsip,我们可以轻松地实现高质量的音频和视频通话。
三、实现会议功能pjsip还支持会议功能,使得多方参与者可以进行音频和视频的交流。
我们可以使用pjsip提供的API来创建一个会议房间,并邀请多个参与者加入。
在会议中,参与者可以进行音频和视频的传输,并进行实时的交流。
pjsip提供了一系列的API,使得我们可以轻松地管理会议的各个方面,包括参与者的加入和退出,音频和视频的传输等。
四、实现网络电话功能pjsip还可以用于实现网络电话功能。
通过pjsip,我们可以将传统的电话系统与IP网络相结合,实现语音通话的网络化。
我们可以使用pjsip提供的API来创建一个软电话,并通过IP网络与其他电话进行通信。
通过pjsip,我们可以实现高质量的语音通话,并享受IP网络带来的便利和灵活性。
五、实现多媒体通信应用除了上述的常见用例,pjsip还可以用于实现各种多媒体通信应用。
例如,我们可以使用pjsip来实现音频或视频的实时流媒体传输,以提供高质量的音频和视频服务。
SIPUserAgent(B2BUAclient)——pjsip
SIPUserAgent(B2BUAclient)——pjsip 1. sip stackspjsip/bell-sip/sofia-sip/libeXosip/libre2. sip user agent and server network architecture3. Installing pjsip on Ubuntusudo apt-get install libasound2-devLinux system has two audio drivers: alsa and oss,oss is old,pjsip supports both,deflaut is oss.如果⽬标系统内核使⽤的是alsa驱动,运⾏例⼦程序的时候会出现以下问题。
alsa_dev.c ! ca_thread_func: error reading data! 解决⽅法修改config_site.h⽂件在pjproject\pjlib\include\pj⽬录下add #define PJMEDIA_AUDIO_DEV_HAS_ALSA 14. cross-compile_install.sh#compiler setupif [[ $1 = 'arm' ]]; thencompiler=`CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++`host=arm-linux-gnueabihfelif [[ $1 = 'himix100' ]]; thencompiler=`CC=arm-himix100-linux-gcc CXX=arm-himix100-linux-g++`host=arm-himix100-linuxelif [[ $1 = 'himix200' ]]; thencompiler=`CC=arm-himix200-linux-gcc CXX=arm-himix200-linux-g++`host=arm-himix200-linuxelse:fi#Preinstalled directoryinstall=${HOME}/_install#Delete installed directoryrm -rf ${install}#library pathpkg_config=${install}/lib/pkgconfiglib_config=`CPPFLAGS=-I${install}/include CFLAGS=-I${install}/include LDFLAGS=-L${install}/lib`#Delete compiled directoryfor i in `ls .`doif [ -d $i ];thenrm -rf $ifidonetar xvf alsa-lib-*cd alsa-lib-*./configure ${compiler} --prefix=${install} --host=${host}make && make installcd ..tar xvf alsa-utils-*cd alsa-utils-*./configure ${compiler} --prefix=${install} --host=${host} PKG_CONFIG_PATH=${pkg_config} ${lib_config} --enable-static --enable-shared --disable-alsamixer --disable-xmlto touch ./alsaconf/po/t-ja.gmotouch ./alsaconf/po/t-ru.gmomake && make installcd ..tar xvf pjproject-*cd pjproject-*./configure ${compiler} --prefix=${install} --host=${host} PKG_CONFIG_PATH=${pkg_config} ${lib_config} --enable-static --enable-shared --disable-libwebrtcmake depmake && make installcd ..export PATH=$PATH:${install}/binexport LD_LIBRARY_PATH=${install}/lib:$LD_LIBRARY_PATHexport ALSA_CONFIG_PATH=${install}/share/alsa/alsa.confdong@ubuntu:~/pjsip$ suPassword:root@ubuntu:/home/dong/pjsip# ./_install.sh armroot@ubuntu:~# tar cvf _install.tar.gz _install/root@ubuntu:~# mv _install.tar.gz /home/dong/dong@ubuntu:~$ sudo chmod 777 _install.tar.gz4)如果⽤的海思arm-himix200-linux-gcc编译器,如果报错的话,可能需要将alsa-utils-1.1.9/alsamixer/alsa-utils-1.1.9/alsamixer/volume_mapping.c⾥的三⾏exp10改成exp5)声卡设备验证#查看⾳频设备dong@dong-ubuntu:~$ arecord -l**** CAPTURE 硬體裝置清單 ****card 0: PCH [HDA Intel PCH], device 0: ALC255 Analog [ALC255 Analog]⼦设备: 1/1⼦设备 #0: subdevice #0#列举出机器所有的声卡dong@dong-ubuntu:~$ cat /proc/asound/cards0 [PCH ]: HDA-Intel - HDA Intel PCHHDA Intel PCH at 0xa4220000 irq 132#列举每个声卡的card number和device numberdong@dong-ubuntu:~$ aplay -l**** PLAYBACK 硬體裝置清單 ****card 0: PCH [HDA Intel PCH], device 0: ALC255 Analog [ALC255 Analog]⼦设备: 1/1⼦设备 #0: subdevice #0card 0: PCH [HDA Intel PCH], device 3: HDMI 0 [HDMI 0]⼦设备: 1/1⼦设备 #0: subdevice #0card 0: PCH [HDA Intel PCH], device 7: HDMI 1 [HDMI 1]⼦设备: 1/1⼦设备 #0: subdevice #0card 0: PCH [HDA Intel PCH], device 8: HDMI 2 [HDMI 2]⼦设备: 1/1⼦设备 #0: subdevice #0card 0: PCH [HDA Intel PCH], device 9: HDMI 3 [HDMI 3]⼦设备: 1/1⼦设备 #0: subdevice #0card 0: PCH [HDA Intel PCH], device 10: HDMI 4 [HDMI 4]⼦设备: 1/1⼦设备 #0: subdevice #0#录⾳dong@dong-ubuntu:~$ arecord -D "plughw:0,0" -f S16_LE -r 16000 -d 5 -t wav file.wav正在录⾳ WAVE 'file.wav' : Signed 16 bit Little Endian, 频率16000Hz, Mono#播放录⾳⽂件dong@dong-ubuntu:~$ aplay file.wav正在播放 WAVE 'file.wav' : Signed 16 bit Little Endian, 频率16000Hz, Mono⼀般alsa设置了⼀个defaults设备,⾳频播放软件默认使⽤defaults设备输出声⾳。
Linux下移植pjsip使用QT开发
/usr/local/pjsip/lib/libpjmedia-codec-arm-none-linux-gnueabi.a \
/deodev-arm-none-linux-gnueabi.a \
/usr/local/pjsip/include/pj/config.h:1165:4: error: #error "PJ_IS_BIG_ENDIAN is not defined!"
解决:在config.h文件中宏定义
#define PJ_IS_BIG_ENDIAN 0
#define PJ_IS_LITTLE_ENDIAN 1
/usr/local/pjsip/lib/libpjnath-arm-none-linux-gnueabi.a \
/usr/local/pjsip/lib/libpjsip-arm-none-linux-gnueabi.a \
/usr/local/pjsip/lib/libpjsip-simple-arm-none-linux-gnueabi.a \
INCLUDEPATH += /usr/local/pjsip/include
DEPENDPATH += /usr/local/pjsip/include
unix:!macx:!symbian: PRE_TARGETDEPS += /usr/local/pjsip/lib/libg7221codec-arm-none-linux-gnueabi.a \
这样就能编译通过了。
将以下库添加到pro文件中
pjsip 工程导读
#include <pj/activesock.h> // 网络相关 #include <pj/addr_resolv.h> // 网络 #include <pj/array.h> // 数组 #include <pj/assert.h> // 断言 #include <pj/ctype.h> // 字符相关 #include <pj/errno.h> // 错误支持 #include <pj/except.h> // 异常支持 #include <pj/fifobuf.h> // 管道缓冲? #include <pj/file_access.h> // IO 支持 #include <pj/file_io.h> // IO 支持 #include <pj/guid.h> // GUID Globally Unique Identifier(全 球唯一标识符) #include <pj/hash.h> // Hash #include <pj/ioqueue.h> // IO 队列 #include <pj/ip_helper.h> // 网络 #include <pj/list.h> // 数据处理 LIST #include <pj/lock.h> // 锁 #include <pj/log.h> // 日志
pjlib-util库
DNS 查询 DNS服务器 Crypto 密码类库 getopt(分析命令行参数的类库) Text scanner 文本扫描 XML 处理 STUN (Simple Traversal of UDP over NATs NAT的UDP简单穿越是一种网 络协议) PCAP ( packet capture library抓包库) HTTP
pjsip源码解读
pjsip源码解读
PJsip 是一个流行的 C++开源协议栈,用于开发 SIP 客户端和服务器。
PJsip 的源代码包含大量的函数和数据结构,因此解读PJsip 的源代码需要一定的编程经验和对 SIP 协议的理解。
在 PJsip 中,最重要的部分是 SIP 协议的处理。
PJsip 使用了一个基于递归的算法来处理 SIP 请求和响应。
该算法基于请求头中的“Request-Line”字段,以确定请求的方法,协议版本和请求头。
然后,算法将递归地处理请求头中的每个字段,并确定响应头和响应体。
PJsip 还提供了许多有用的函数和数据结构,以支持 SIP 协议的处理。
例如,PJsip 提供了一些用于构建 SIP 请求和响应的函数,包括构建请求头和响应头。
PJsip 还提供了一些用于处理 SIP 事件和通知的函数。
总结起来,解读 PJsip 的源代码需要对 SIP 协议和 C++编程有一定的了解。
PJsip 提供了大量的函数和数据结构,用于支持 SIP 协议的处理,因此解读 PJsip 的源代码对于开发 SIP 客户端和服务器非常有用。
PJSIP开发指南
PJSIP开发指南⼀、通⽤设计1.1 架构1.1.1 通信图下⾯的图展⽰了SIP消息在PJSIP组件间从后端到前端如何传递的。
1.1.2 类图下⾯的图显⽰类视图1.2 EndpointSIP 协议栈的核⼼是SIP endpoint,它由透明的pjsip_endpoint的表⽰,endpoint具有下⾯的属性和职责l 内存储⼯⼚,为所有的SIP组件分配内存l 具备定时器堆实列,为所有的SIP组件调度定时器l 传输管理起实例,传输管理器负责传输SIP消息并且控制消息的解析和打印。
l 拥有单实例PJLIB io 队列,IO队列采⽤proactor模式分发事件。
l 提供了线程安全的轮询功能,应⽤程序的线程能够查询定时器和socket事件(PJSIP⾃⾝不常见任何线程)l 管理PJSIP模块,PJSIP模块主要以为着扩展SIP stack,⽽不仅仅限于消息的解析和打印。
l 从传输模块接收SIP消息,并且分发到各个模块中。
1.2.1 内存池分配和释放为了保证线程的安全性以及在整个应⽤中策略强⼀致性,所有的SIP内存都需要在endpoint中完成分配。
⽐如:内存池缓存,未使⽤的内存被保留在以后使⽤⽽不是销毁。
Endpoint提供下⾯的函数分配和释放内存池pjsip_endpt_create_pool()pjsip_endpt_release_pool()当endpoint被pjsip_endpt_create()创建时,应⽤⼀定要指明由endpoint使⽤的内存池⼯⼚。
在整个⽣命周期内Endpoint持有内存池⼯⼚的指针,将来备⽤创建和释放内存。
1.2.2 定时器管理Endpoint 拥有⼀个独⽴定时器堆实例,所有SIP组件的定时器创建和调度都需要通过endpoint 完成。
Endpoint提供下⾯的函数管理定时器pjsip_endpt_schedule_timer()pjsip_endpt_cancel_timer()endpoint的poll函数被调⽤时,检查定时器是否过期。
几种开源SIP协议栈对比
几种开源SIP协议栈对比1.PJSIP:PJSIP是一个强大而灵活的开源SIP协议栈,提供了全面的SIP协议支持以及音频、视频、实时通信等功能。
它使用C语言编写,具有跨平台的特性,支持多种操作系统和开发环境,如Windows、Linux、macOS等。
PJSIP提供了简单易用的API,使开发者能够快速构建VoIP应用程序。
2. Linphone:Linphone是一个流行的开源SIP协议栈,支持语音、视频、实时消息等多媒体通信功能。
它使用C语言编写,可跨平台运行于多种操作系统和设备,如Windows、Linux、iOS和Android。
Linphone具有丰富的功能和友好的用户界面,让开发者和用户能够轻松地构建和使用VoIP应用程序。
3. Doubango:Doubango是一个高性能的开源SIP协议栈,专注于提供低延迟和高质量的音频和视频通信服务。
它使用C++语言编写,提供了跨平台的支持,可运行于不同的操作系统和设备。
Doubango提供了可扩展的API,使开发者能够快速构建各种实时通信应用程序。
4. Sofia-SIP:Sofia-SIP是一个轻量级的开源SIP协议栈,专注于提供简单和可移植的SIP协议支持。
它使用C语言编写,具有可扩展性和灵活性,可适应不同的应用需求。
Sofia-SIP支持多种操作系统和开发环境,如Linux、Windows、macOS和iOS等。
5.JsSIP:JsSIP是一个基于JavaScript的开源SIP协议栈,专为Web应用程序而设计。
它使用纯粹的JavaScript语言编写,运行于现代的Web浏览器环境中。
JsSIP提供了易于使用的API,使开发者能够在Web应用程序中集成SIP功能,实现浏览器间的实时通信。
综上所述,开源SIP协议栈提供了各种选择,适用于不同的应用需求和开发环境。
无论是构建VoIP应用程序还是实现WebRTC等实时通信功能,开发者都可以根据自身需求选择适合的开源SIP协议栈来实现他们的目标。
iphone实时通话开源框架pjsip编译
iphone实时通话开源框架pjsip编译iphone实时通话开源框架pjsip编译-pjsua运行测试zxwo0oiphonepjsip, pjsua0 Comments发表评论iphone 实时通话开源框架中,pjsip 是一个比较精简的好框架,比linphone好编译多了。
下面介绍下编译运行步骤。
一、编译运行环境:iPhone :5.1.1,系统:10.7.3 , xcode: 4.5.2二、准备工作:1、从pjsip官网下载tar的源代码,解压到本地;2、在目录/pjsip/pjlib/include/pj/中新建config_site.h,粘贴如下代码入内:#define PJ_CONFIG_IPHONE 1#include <pj/config_site_sample.h>三、编译pjsip:在终端中运行如下命令:$ cd /path/to/your/pjsip/$ ./configure-iphone$ make dep && make clean && make四、编译pjsua Demo工程:打开目录pjproject/pjsip-apps/src/ipjsua中的项目,连接上你的iphone编译运行就行了。
五、测试通话:1、VoIP服务器:可以使用MiniSipServer免费版,安装很简单,安装完后别忘记了新建账户(这里以100和101为例)。
2、VoIP客户端:个人感觉X-lite 比较好用,不管是mac版本还是windows 版本。
登录账户成功后会显示注册成功.3、在iPhone的pjsip界面中登录账户101:+aYour SIP URL: (empty to cancel): sip:101@192.168.1.1URL of the registrar: (empty to cancel): sip:192.168.1.1 Auth Realm: (empty to cancel): *Auth Username: (empty to cancel): 101Auth Password: (empty to cancel): 123456和100打电话:+bEnter buddy's URI: (empty to cancel):sip:100@192.168.1.1mMake call: 1打电话操作还可以简化:mMake call: sip:100@192.168.1.1另外还可以将相关信息添加到配置文中:在(alice.cfg)添加内容:# This is a comment in the config file.--id sip:alice@--registrar sip:--realm *--username alice--password secret=================================配置文件其他用法简介:用法:PJSUA [选项] [SIP的URL调用]一般选项:--config-file=file 读取从文件的配置/参数;--help 显示此帮助屏幕;--version 显示版本信息;日志记录选项:--log-file=fname 日志文件名(默认是stderr);--log-level=N 设置日志的最大级别为N(0(无)6(跟踪))(默认值= 5);--app-log-level=N 设置日志的最大水平为stdout显示(默认值= 4);--color 运用丰富多彩的日志(在Win32默认开启);--no-color 禁用丰富多彩的日志;--light-bg 使用白底黑字的颜色(默认是黑暗的背景);SIP帐户选项:--use-ims 开启和这个账号相关的3GPP/IMS设置;--use-srtp=N 是否使用SRTP? 0:不使用, 1:可选, 2:强制使用(默认:0);--srtp-secure=N SRTP 是否需要安全的SIP? 0:不需要, 1:tls方式, 2:sips (默认:1);--registrar=url 设置注册服务器的URL;--id=url 设置本地账户的URL--contact=url 选择性的覆盖联系人信息--contact-params=S 给指定的联系URI添加S参数--proxy=url 可选择的访问代理服务器的URL--reg-timeout=SEC 注册时间间隔(default 55)--realm=string 设置域--username=string 设置用户名--password=string 设置密码--publish 发PUBLISH--use-100rel 需要可靠的临时响应(100rel)--auto-update-nat=N n为0或1来启用/禁用SIP遍历后面对称NAT(默认1)--next-cred 添加其他凭据SIP帐户控制:--next-account 添加更多的账户传输选项:--ipv6 使用IPv6--local-port=port 端口--ip-addr=IP ip地址--bound-addr=IP 绑定端口--no-tcp 禁用TCP传输--no-udp 禁用UDP传输--nameserver=NS 域名服务器--outbound=url 设置全局代理服务器的URL,可以指定多次--stun-srv=name 设置STUN服务器主机或域名TLS选项:--use-tls 启用TLS传输(默认不开启)--tls-ca-file 指定TLS CA文件(默认为无)--tls-cert-file 指定TLS证书文件(默认为无)--tls-privkey-file 指定TLS私钥文件(默认值=无)--tls-password 指定TLS私钥文件密码(默认为无)--tls-verify-server 验证服务器的证书(默认=没有)--tls-verify-client 验证客户端的证书(默认=没有)--tls-neg-timeout 指定超时(默认值无)--tls-srv-name 指定TLS服务器名称为多宿主服务器(可选)媒体选项:--add-codec=name 手工添加编解码(默认开启所有)--dis-codec=name 禁用某个编解码--clock-rate=N 覆盖会议桥时钟频率--snd-clock-rate=N 覆盖音频设备时钟频率--stereo 音频设备及会议桥开通立体声模式--null-audio 使用NULL音频设备--play-file=file 在会议桥中注册WAV文件--play-tone=FORMAT 向会议桥注册音调,格式是'F1,F2,ON,OFF',其中F1,F2为频率,ON,OFF=on/off ,可以指定多次。
pjsip开发手册之模块
pjsip开发手册之模块一、概述pjsip是一个开源的基于SIP协议的通信软件框架,广泛应用于VoIP、多媒体会议、即时通讯等领域。
本开发手册之模块部分将介绍pjsip框架中各个模块的功能、接口和使用方法,帮助开发者更好地理解和使用pjsip。
二、模块介绍1.媒体模块(Media):pjsip媒体模块提供了音频和视频编解码、流量控制、多路复用等媒体处理功能,支持多种编解码器和网络协议。
开发者可以使用该模块实现音频和视频的传输和处理。
2.信令模块(SIP):pjsip信令模块实现了基于SIP协议的会话建立和终止过程。
开发者可以使用该模块实现基于SIP协议的通信应用,如VoIP、多媒体会议、即时通讯等。
3.传输模块(Transport):pjsip传输模块提供了多种网络传输方式,如TCP、UDP、TLS等,支持多协议栈和多播功能。
开发者可以使用该模块实现不同网络环境下的通信应用。
4.用户模块(User):pjsip用户模块提供了对用户账号和会话的管理功能,支持用户认证、会话记录和统计等功能。
开发者可以使用该模块实现通信应用的用户管理和计费功能。
5.插件模块(Plugins):pjsip插件模块提供了对第三方插件的支持,如音视频编解码插件、网络协议插件等。
开发者可以使用该模块扩展pjsip的功能,实现更加丰富的通信应用。
三、接口使用1.媒体模块:开发者可以使用pjsip媒体模块提供的API进行音频和视频的编解码、多路复用等操作。
具体接口包括:media_manager、codec_factory等。
2.信令模块:开发者可以使用pjsip信令模块提供的API建立和终止SIP会话。
具体接口包括:sip_session、endpoint等。
3.传输模块:开发者可以使用pjsip传输模块提供的API进行网络传输控制和管理。
具体接口包括:transport_manager、address_resolver等。
4.用户模块:开发者可以使用pjsip用户模块提供的API进行用户账号和会话的管理。
教你编译pjsip源码的方法
2. 使用 VS2015编译
双击pjproject-vs14.sln 为了防止编译报错,首先进入这个目录: pjproject-2.10\pjlib\include\pj 复制 config_site_sample.h 这个文件 config_site.h
教你编译 pjsip源码的方法
编译整个解决方案: pjsua所在目录:
本文涉pjsip代码及预编译程序下载地址: 到此这篇关于教你编译pjsip源码的方法的文章就介绍到这了,更多相关编译pjsip源码内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
Байду номын сангаас
这篇文章主要介绍了python基于kmeans聚类算法的图像分割文中通过示例代码介绍的非常详细对大家的学习或者工作具有一定的参考学习价值需要的朋友们下面随着小编来一起学习学习吧
操作系统 : Windows 10_x64 [版本 10.0.19042.685] pjsip版本 : 2.10
1. 下载 pjsip源代码
PJSIP介绍
PJLIB基础框架库提供的功能
内存的处理、 内存的处理、数据的存储 .数据结构的(hash表、link表、二叉树、等) .caching和pool;缓冲池和内存池 OS抽象 OS抽象 .线程、互斥、临界区、锁对象、事件对象 .定时器 .pj_str_t字符串 操作系统级别的函数抽象 .socket的抽象(tcp/udp) .文件的读写 使用前的初始化, 使用前的初始化,使用后的清理
PJSIP 2
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[基础框架库]
PJSIP
11
Diagram: Libraries Architecture
PJSIP
12
Questions
Thanks
支持多种SIP功能及扩展功能 丰富的文档资料
多种SIP功能和扩展功能,例如多人会话,事件驱动框架,会话控制 (presence),即时信息,电话传输,等等在库文件里得以实现。 PJSIP开发人员提供了大量的极有价值的文档资料供大家使用。
PJSIP 4
PJMEDIA简介 简介
PJMEDIA是一个为PJSIP建立一个完整特性SIP用户代 理应用提供的补充库,这些应用包括: softphones/hardphones,gateways or B2BUA. 使用PJSIP与PJMEDIA一起开发的应用,具备如下的 特性:
北邮研究生计算机网络VOIPSIP实验报告
计算机网络实验课程报告课题:SIP客户端的开源实现姓名张涛学院网络技术研究院班级学号注册组号2015年11月21日1.小组信息2.实验目的1)理解VOIP,SIP技术,用开源代码实现一个SIP客户端(PJSIP)2)用实现的客户端完成在SIP呼叫中心上的注册和测试3.实验背景知识3.1.阅读VOIP,SIP技术相关内容,加深对VOIP技术原理的理解.1)VOIP技术原理在现在的网络通信中,Email服务已经不是现在首选的通信方式了更多的即时通信,语音服务等,在网络上面层出不穷VoIP传统的电话网是以电路交换方式传输语音,所要求的传输宽带为64kbit/s而所谓的VoIP是以IP分组交换网络为传输平台,对模拟的语音信号进行压缩打包等一系列的特殊处理,使之可以采用无连接的UDP协议进行传输为了在一个IP网络上传输语音信号,要求几个元素和功能最简单形式的网络由两个或多个具有VoIP功能的设备组成,这一设备通过一个IP网络连接VoIP设备是如何把语音信号转换为IP数据流,并把这些数据流转发到IP目的地,IP目的地又把它们转换回到语音信号两者之音的网络必须支持IP传输,且可以是IP路由器和网络链路的任意组合因此可以简单地将VoIP的传输过程分为下列几个阶段语音—数据转换语音信号是模拟波形,通过IP方式来传输语音,不管是实时应用业务还是非实时应用业务,首先要对语音信号进行模拟数据转换,也就是对模拟语音信号进行8位或6位的量化,然后送入到缓冲存储区中,缓冲器的大小可以根据延迟和编码的要求选择许多低比特率的编码器是采取以帧为单位进行编码典型帧长为10 30ms考虑传输过程中的代价,语间包通常由60120或240ms的语音数据组成数字化可以使用各种语音编码方案来实现,目前采用的语音编码标准主要有ITU-T G。
711源和目的地的语音编码器必须实现相同的算法,这样目的地的语音设备帮可以还原模拟语音信号原数据到IP转换一旦语音信号进行数字编码,下一步就是对语音包以特定的帧长进行压缩编码大部份的编码器都有特定的帧长,若一个编码器使用15ms的帧,则把从第一来的60ms的包分成4帧,并按顺序进行编码每个帧合120个语音样点(抽样率为8kHz)编码后,将4个压缩的帧合成一个压缩的语音包送入网络处理器网络处理器为语音添加包头时标和其它信息后通过网络传送到另一端点语音网络简单地建立通信端点之间的物理连接(一条线路),并在端点之间传输编码的信号IP网络不像电路交换网络,它不形成连接,它要求把数据放在可变长的数据报或分组中,然后给每个数据报附带寻址和控制信息,并通过网络发送,一站一站地转发到目的地传送在这个通道中,全部网络被看成一个从输入端接收语音包,然后在一定时间(t)内将其传送到网络输出端t可以在某全范围内变化,反映了网络传输中的抖动网络中的同间节点检查每个IP数据附带的寻址信息,并使用这个信息把该数据报转发到目的地路径上的下一站网络链路可以是支持IP数据流的任何拓结构或访问方法IP包—数据的转换目的地VoIP设备接收这个IP数据并开始处理网络级提供一个可变长度的缓冲器,用来调节网络产生的抖动该缓冲器可容纳许多语音包,用户可以选择缓冲器的大小小的缓冲器产生延迟较小,但不能调节大的抖动其次,解码器将经编码的语音包解压缩后产生新的语音包,这个模块也可以按帧进行操作,完全和解码器的长度相同若帧长度为15ms,,是60ms的语音包被分成4帧,然后它们被解码还原成60ms的语音数据流送入解码缓冲器在数据报的处理过程中,去掉寻址和控制信息,保留原始的原数据,然后把这个原数据提供给解码器数字语音转换为模拟语音播放驱动器将缓冲器中的语音样点(480个)取出送入声卡,通过扬声器按预定的频率(例如8kHz)播出简而言之,语音信号在IP网络上的传送要经过从模拟信号到数字信号的转换数字语音封装成IP分组IP分组通过网络的传送IP分组的解包和数字语音还原到模拟信号等过程。
pjsip 实例
pjsip 实例什么是pjsip?pjsip是一个开源的多媒体通信库,用于实现VoIP(Voice over IP)和视频通话的功能。
它提供了丰富的API和协议支持,可以在各种平台上进行开发和部署,包括Windows、Linux、iOS和Android等。
pjsip采用C语言编写,具有轻量级、高性能和可移植性的特点。
它支持多种音频和视频编解码器,包括G.711、G.722、Opus、H.264等。
此外,pjsip还提供了丰富的网络协议支持,包括SIP、SDP、RTP、STUN、ICE等,使得开发者可以通过pjsip实现各种复杂的通信功能。
pjsip的主要特性1.SIP协议支持:pjsip完全支持SIP(Session Initiation Protocol)协议,包括SIP的请求和响应消息、SIP的状态机、SIP的认证和鉴权等。
开发者可以使用pjsip构建一个完整的SIP通信系统。
2.音频和视频支持:pjsip支持多种音频和视频编解码器,可以进行音频和视频的传输和处理。
开发者可以使用pjsip实现语音通话、视频通话、音频会议等功能。
3.网络协议支持:pjsip提供了丰富的网络协议支持,包括UDP、TCP、TLS等传输协议,以及STUN、ICE等NAT穿越协议。
这些协议可以帮助开发者解决网络通信中的各种问题,如NAT穿越、防火墙遍历等。
4.多平台支持:pjsip可以在多种平台上进行开发和部署,包括Windows、Linux、iOS和Android等。
开发者可以根据自己的需求选择合适的平台进行开发,并且可以方便地进行平台间的移植和调试。
5.灵活的配置选项:pjsip提供了丰富的配置选项,可以根据不同的需求进行灵活的配置。
开发者可以根据自己的需求选择合适的配置选项,并且可以根据需要进行自定义配置。
pjsip的使用示例下面是一个简单的使用pjsip的示例,演示了如何创建一个SIP用户代理(User Agent)并进行呼叫操作:#include <pjsua-lib/pjsua.h>int main() {// 初始化pjsua库pjsua_config cfg;pjsua_logging_config log_cfg;pjsua_config_default(&cfg);pjsua_logging_config_default(&log_cfg);pjsua_create(&cfg, &log_cfg, NULL);// 启动pjsua库pjsua_start();// 添加一个SIP账号pjsua_acc_add(...);// 呼叫一个电话号码pjsua_call_make_call(...);// 处理事件循环pjsua_event_loop();// 销毁pjsua库pjsua_destroy();return 0;}在这个示例中,我们首先初始化了pjsua库,并且启动了pjsua库。
开源pjsip使用介绍
开源pjsip使用介绍开源pjsip使用介绍一、编译应用程序1、源码包下载,官网:2、源码包编译:tar –jxvf pjproject-2.3.tar.bz2cd pjproject-2.3./configure --host=arm-none-linux-gnueabimake dep&&make clean&&make注:arm-none-linux-gnueabi为配置的交叉编译器3、应用程序目录pjproject-2.3/ pjsip-apps/bin/应用程序为:pjsua-arm-none-linux-gnueabi4、在IPC上运行./pjsua --registrar=sip:112.126.75.216 --id=sip:**************.75.216--realm=*--username=100000--password=103852 --auto-answer=200 --dis-codec=speex/16000 –dis-codec=speex/8000 --dis-codec=speex/32000 --dis-codec=iLBC/8000 --dis-codec=GSM/8000 --clock-rate=8000 --snd-clock-rate=8000 --no-vad --ec-opt=2二、应用程序命令操作1、不经过sip协议的服务器,处于同一网段。
(1)呼叫1) +b (+b为添加一个联系伙伴)2) sip:对方IP:端口 (例:sip:10.10.108.110:5060,伙伴的URL)3) m (m为发起一个拨号)4) 1 (选择一个要拨通的伙伴,1号伙伴)(2)、接听1) a (进入选项操作界面)2) 200 (输入200为接听)2、经过sip服务器(1)呼叫1)+a (注册一个账户)2)sip:要注册的账号@服务器IP:端口(例:sip:************.142.173:5060)3)*4)1000 (账号)5)1234 (密码)6)sip:对方账号@服务器IP:端口(例:sip:************.142.173)(2)、接听1) a (进入选项操作界面)2) 200 (输入200为接听)三、自动接听通过服务器拨打过来的电话./pjsua --registrar=sip:112.126.75.216 --id=sip:**************.75.216--realm=*--username=100000--password=103852 --auto-answer=200 --dis-codec=speex/16000 –dis-codec=speex/8000 --dis-codec=speex/32000 --dis-codec=iLBC/8000 --dis-codec=GSM/8000 --clock-rate=8000 --snd-clock-rate=8000 --no-vad --ec-opt=2四、Sip服务器目前两种流行的Sip服务器源码下载网站:1、freeswitch官网介绍2、opensipspub/opensips/官网介绍。
PJSIP简介配置和PJLIB基础库的使用
PJSIP简介配置和PJLIB基础库的使用PJSIP的实现是为了能在嵌入式设备上高效实现SIP/VOIP.1.PJSIP库的主要特征:1).极具移植性.(Extremely portable)当前可支持平台包括:* Win32/x86 (Win95/98/ME, NT/2000/XP/2003, mingw).* arm, WinCE and Windows Mobile.* Linux/x86, (user mode and as kernel module(!)).* Linux/alpha* Solaris/ultra.* MacOS X/powerpc* RTEMS (x86 and powerpc).* Symbian OS2).非常小的足印.(Very small footprint)官方宣称编译后的库<150Kb,我在PC上编译后加上strip后大概173Kb,这对于嵌入式设备,是个好消息3).高性能.(High performance)这点我们后面可以看看是否如作者宣称的2. PJSIP的组成.其实说是PJSIP不是特别贴切,这个库实际上是几个部分组成的.1).PJSIP - Open Source SIP Stack[开源的SIP协议栈]2).PJMEDIA - Open Source Media Stack[开源的媒体栈]3).PJNATH - Open Source NAT Traversal Helper Library[开源的NAT-T****库]4).PJLIB-UTIL - Auxiliary Library[****工具库]5).PJLIB - Ultra Portable Base Framework Library[基础框架库]PJLIB-UTIL****工具库:加解密MD5和CRC32的算法PJNATH开源的NAT库包含ICE打洞PJSUA-LIB库:最顶层的SIP库支持VOIPPJMEDIA库:最顶层的支持视频的库3. PJLIB基础框架库提供的功能:1).内存的处理、数据的存储.数据结构的(hash表、link表、二叉树、等).caching和pool;缓冲池和内存池2).OS抽象.线程、互斥、临界区、锁对象、事件对象.定时器.pj_str_t字符串3).操作系统级别的函数抽象.socket的抽象(tcp/udp).文件的读写4).使用前的初始化,使用后的清理4.PJSIP的安装和配置:1.安装和编辑pjsip库:A.将pjproject-1.5.zip和DirectX-Lib.rar解压到当前目录的pjlib 路径下;B.查看pjlib\pjproject-1.5\pjlib\include\pj下面是否有config_site.h,如果没有建一个空文件C.将DirectX的x86的lib库拷贝到C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\LibD.在环境变量中设置DXSDK_DIR=direx中的include目录E.首先编译pjlib\pjproject-1.5\pjproject-vs8.sln [win32]F.然后打开工程开始编译2.脱离pjsip环境的工程的配置需要头文件的可以加入$(SolutionDir)pjlib\pjproject-1.5\pjlib\include$(SolutionDir)pjlib\pjproject-1.5\pjlib-util\include$(SolutionDir)pjlib\pjproject-1.5\pjnath\include$(SolutionDir)pjlib\pjproject-1.5\pjsip\include$(SolutionDir)pjlib\pjproject-1.5\pjmedia\include如果需要lib库文件的加入$(SolutionDir)pjlib\pjproject-1.5\pjlib\lib$(SolutionDir)pjlib\pjproject-1.5\pjlib-util\lib$(SolutionDir)pjlib\pjproject-1.5\pjnath\lib$(SolutionDir)pjlib\pjproject-1.5\pjsip\lib$(SolutionDir)pjlib\pjproject-1.5\pjmedia\lib$(SolutionDir)pjlib\pjproject-1.5\lib我的程序所依赖PJLIB的库有:pjlib-i386-Win32-vc8-Debug.libpjlib-util-i386-Win32-vc8-Debug.libpjnath-i386-Win32-vc8-Debug.libpjsip-core-i386-Win32-vc8-Debug.libpjsip-simple-i386-Win32-vc8-Debug.libpjsip-ua-i386-Win32-vc8-Debug.libpjsua-lib-i386-Win32-vc8-Debug.libpjmedia-audiodev-i386-Win32-vc8-Debug.libpjmedia-codec-i386-Win32-vc8-Debug.libpjmedia-i386-Win32-vc8-Debug.liblibpjproject-i386-Win32-vc8-Debug.lib1.使用前的初始化和使用后的清理PJSIP库里面封装了很多线程内存池;而且很多对象都是基于内存池创建的,所以几乎所以的库都需要初始化或创建下面是pjlib,pjlib-util,pjnath,pjsua-lib库的初始化和关闭显示行号复制代码这是一段程序代码。
使用pjsip代码独立开发
使⽤pjsip代码独⽴开发1、在不改动pjsip代码的情况下,和pjsip⼯程⽬录并⾏建⽴win32控制台程序⼯程P2PTraversal⽬录结构如下:.├── pjproject-2.6└── pjsipdemo2、在VS2008下,新建项⽬3、⼯程引⼊pjsip的相关配置本例按照引⼊pjlib、pjlib-util、pjnath三个模块进⾏设置,如果引⼊更多需要参考如下设置增加对应的lib或.h⽬录设置①增加“附加包含⽬录”(针对引⽤头⽂件)为了减少配置次数,在配置页⾯选择“所有配置”,可以⼀次性将“Debug”和“Release”全部配置好。
主要是针对头⽂件,静态库则需要分别设置。
在⼯程属性页⾯中,找到C/C++选项的常规配置,在附加包含⽬录中添加如下路径(相对路径,按照⽬录层次关系)../pjproject-2.6/pjlib/include/../pjproject-2.6/pjlib-util/include/../pjproject-2.6/pjnath/include/②设置“附加库⽬录”../pjproject-2.6/pjlib/lib/../pjproject-2.6/pjlib-util/lib/../pjproject-2.6/pjnath/lib/③设置“附加依赖项”Debug设置:ws2_32.libpjlib-i386-Win32-vc8-Debug.libpjlib-util-i386-Win32-vc8-Debug.libpjnath-i386-Win32-vc8-Debug.libRelease设置ws2_32.libpjlib-i386-Win32-vc8-Release.libpjlib-util-i386-Win32-vc8-Release.libpjnath-i386-Win32-vc8-Release.lib4、在main.cpp⽂件中加⼊实现(以改造的icedemo为例)1/* $Id: icedemo.c 4624 2013-10-21 06:37:30Z ming $ */2/*3* Copyright (C) 2008-2011 Teluu Inc. ()4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License as published by7* the Free Software Foundation; either version 2 of the License, or8* (at your option) any later version.9*10* This program is distributed in the hope that it will be useful,11* but WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13* GNU General Public License for more details.14*15* You should have received a copy of the GNU General Public License16* along with this program; if not, write to the Free Software17* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18*/19 #include <stdio.h>20 #include <stdlib.h>21 #include <pjlib.h>22 #include <pjlib-util.h>23 #include <pjnath.h>242526#define THIS_FILE "icedemo.c"2728/* For this demo app, configure longer STUN keep-alive time29* so that it does't clutter the screen output.30*/31#define KA_INTERVAL 300323334/* This is our global variables */35static struct app_t36 {37/* Command line options are stored here */38struct options39 {40 unsigned comp_cnt;41 pj_str_t ns;42int max_host;43 pj_bool_t regular;44 pj_str_t stun_srv;45 pj_str_t turn_srv;46 pj_bool_t turn_tcp;47 pj_str_t turn_username;48 pj_str_t turn_password;49 pj_bool_t turn_fingerprint;50const char *log_file;51 } opt;5253/* Our global variables */54 pj_caching_pool cp;55 pj_pool_t *pool;56 pj_thread_t *thread;57 pj_bool_t thread_quit_flag;58 pj_ice_strans_cfg ice_cfg;59 pj_ice_strans *icest;60 FILE *log_fhnd;6162/* Variables to store parsed remote ICE info */63struct rem_info6465 {66char ufrag[80];67char pwd[80];68 unsigned comp_cnt;69 pj_sockaddr def_addr[PJ_ICE_MAX_COMP];70 unsigned cand_cnt;71 pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND];72 } rem;7374 } icedemo;7576/* Utility to display error messages */77static void icedemo_perror(const char *title, pj_status_t status)78 {79char errmsg[PJ_ERR_MSG_SIZE];8081 pj_strerror(status, errmsg, sizeof(errmsg));82 PJ_LOG(1,(THIS_FILE, "%s: %s", title, errmsg));83 }8485/* Utility: display error message and exit application (usually86* because of fatal error.87*/88static void err_exit(const char *title, pj_status_t status)89 {90if (status != PJ_SUCCESS)91 {92 icedemo_perror(title, status);93 }94 PJ_LOG(3,(THIS_FILE, "Shutting down.."));9596if (icedemo.icest)97 pj_ice_strans_destroy(icedemo.icest);9899 pj_thread_sleep(500);100101 icedemo.thread_quit_flag = PJ_TRUE;102if (icedemo.thread)103 {104 pj_thread_join(icedemo.thread);105 pj_thread_destroy(icedemo.thread);106 }107108if (icedemo.ice_cfg.stun_cfg.ioqueue)109 pj_ioqueue_destroy(icedemo.ice_cfg.stun_cfg.ioqueue);110111if (icedemo.ice_cfg.stun_cfg.timer_heap)112 pj_timer_heap_destroy(icedemo.ice_cfg.stun_cfg.timer_heap);113114 pj_caching_pool_destroy(&icedemo.cp);115116 pj_shutdown();117118if (icedemo.log_fhnd)119 {120 fclose(icedemo.log_fhnd);121 icedemo.log_fhnd = NULL;122 }123124 exit(status != PJ_SUCCESS);125 }126127#define CHECK(expr) status=expr; \128if (status!=PJ_SUCCESS) { \129 err_exit(#expr, status); \130 }131132/*133* This function checks for events from both timer and ioqueue (for134* network events). It is invoked by the worker thread.135*/136static pj_status_t handle_events(unsigned max_msec, unsigned *p_count) 137 {138enum { MAX_NET_EVENTS = 1 };139 pj_time_val max_timeout = {0, 0};140 pj_time_val timeout = { 0, 0};141 unsigned count = 0, net_event_count = 0;142int c;143144 max_timeout.msec = max_msec;145146/* Poll the timer to run it and also to retrieve the earliest entry. */147 timeout.sec = timeout.msec = 0;148 c = pj_timer_heap_poll( icedemo.ice_cfg.stun_cfg.timer_heap, &timeout ); 149if (c > 0)150 count += c;151152/* timer_heap_poll should never ever returns negative value, or otherwise 153 * ioqueue_poll() will block forever!154*/155 pj_assert(timeout.sec >= 0 && timeout.msec >= 0);156if (timeout.msec >= 1000) timeout.msec = 999;157158/* compare the value with the timeout to wait from timer, and use the159 * minimum value.160*/161if (PJ_TIME_VAL_GT(timeout, max_timeout))162 timeout = max_timeout;163164/* Poll ioqueue.165 * Repeat polling the ioqueue while we have immediate events, because166 * timer heap may process more than one events, so if we only process167 * one network events at a time (such as when IOCP backend is used),168 * the ioqueue may have trouble keeping up with the request rate.169 *170 * For example, for each send() request, one network event will be171 * reported by ioqueue for the send() completion. If we don't poll172 * the ioqueue often enough, the send() completion will not be173 * reported in timely manner.174*/175do176 {177 c = pj_ioqueue_poll( icedemo.ice_cfg.stun_cfg.ioqueue, &timeout);178if (c < 0)179 {180 pj_status_t err = pj_get_netos_error();181 pj_thread_sleep(PJ_TIME_VAL_MSEC(timeout));182if (p_count)183 *p_count = count;184return err;185 }186else if (c == 0)187 {188break;189 }190else191 {192 net_event_count += c;193 timeout.sec = timeout.msec = 0;194 }195 } while (c > 0 && net_event_count < MAX_NET_EVENTS);196197 count += net_event_count;198if (p_count)199 *p_count = count;200201return PJ_SUCCESS;202203 }204205/*206* This is the worker thread that polls event in the background.207*/208static int icedemo_worker_thread(void *unused)209 {210 PJ_UNUSED_ARG(unused);211212while (!icedemo.thread_quit_flag)213 {214 handle_events(500, NULL);215 }216217return0;218 }219220/*221* This is the callback that is registered to the ICE stream transport to222* receive notification about incoming data. By "data" it means application223* data such as RTP/RTCP, and not packets that belong to ICE signaling (such 224* as STUN connectivity checks or TURN signaling).225*/226static void cb_on_rx_data(pj_ice_strans *ice_st,227 unsigned comp_id,228void *pkt, pj_size_t size,229const pj_sockaddr_t *src_addr,230 unsigned src_addr_len)231 {232char ipstr[PJ_INET6_ADDRSTRLEN+10];233234 PJ_UNUSED_ARG(ice_st);235 PJ_UNUSED_ARG(src_addr_len);236 PJ_UNUSED_ARG(pkt);237238// Don't do this! It will ruin the packet buffer in case TCP is used!239//((char*)pkt)[size] = '\0';240241 PJ_LOG(3,(THIS_FILE, "Component %d: received %d bytes data from %s: \"%.*s\"", 242 comp_id, size,243 pj_sockaddr_print(src_addr, ipstr, sizeof(ipstr), 3),244 (unsigned)size,245 (char*)pkt));246 }247248/*249* This is the callback that is registered to the ICE stream transport to250* receive notification about ICE state progression.251*/252static void cb_on_ice_complete(pj_ice_strans *ice_st,253 pj_ice_strans_op op,254 pj_status_t status)255 {256const char *opname =257 (op==PJ_ICE_STRANS_OP_INIT? "initialization" :258 (op==PJ_ICE_STRANS_OP_NEGOTIATION ? "negotiation" : "unknown_op"));259260if (status == PJ_SUCCESS)261 {262 PJ_LOG(3,(THIS_FILE, "ICE %s successful", opname));263 }264else265 {266char errmsg[PJ_ERR_MSG_SIZE];267268 pj_strerror(status, errmsg, sizeof(errmsg));269 PJ_LOG(1,(THIS_FILE, "ICE %s failed: %s", opname, errmsg));270 pj_ice_strans_destroy(ice_st);271 icedemo.icest = NULL;272 }273 }274275/* log callback to write to file */276static void log_func(int level, const char *data, int len)277 {278 pj_log_write(level, data, len);279if (icedemo.log_fhnd)280 {281if (fwrite(data, len, 1, icedemo.log_fhnd) != 1)282return;283 }284 }285286/*287* This is the main application initialization function. It is called288* once (and only once) during application initialization sequence by289* main().290*/291static pj_status_t icedemo_init(void)292 {293 pj_status_t status;294295296//设置⽇志⽂件路径297 icedemo.opt.log_file = pj_str(".\\Demo.log").ptr;298 pj_log_set_level(6);299300if (icedemo.opt.log_file)301 {302 icedemo.log_fhnd = fopen(icedemo.opt.log_file, "a");303 pj_log_set_log_func(&log_func);304 }305306/* Initialize the libraries before anything else */307 CHECK( pj_init() );308 CHECK( pjlib_util_init() );309 CHECK( pjnath_init() );310311/* Must create pool factory, where memory allocations come from */312 pj_caching_pool_init(&icedemo.cp, NULL, 0);313314/* Init our ICE settings with null values */315 pj_ice_strans_cfg_default(&icedemo.ice_cfg);316317 icedemo.ice_cfg.stun_cfg.pf = &icedemo.cp.factory;318319/* Create application memory pool */320 icedemo.pool = pj_pool_create(&icedemo.cp.factory, "icedemo",321512, 512, NULL);322323/* Create timer heap for timer stuff */324 CHECK( pj_timer_heap_create(icedemo.pool, 100,325 &icedemo.ice_cfg.stun_cfg.timer_heap) );326327/* and create ioqueue for network I/O stuff */328 CHECK( pj_ioqueue_create(icedemo.pool, 16,329 &icedemo.ice_cfg.stun_cfg.ioqueue) );330331/* something must poll the timer heap and ioqueue,332 * unless we're on Symbian where the timer heap and ioqueue run333 * on themselves.334*/335 CHECK( pj_thread_create(icedemo.pool, "icedemo", &icedemo_worker_thread, 336 NULL, 0, 0, &icedemo.thread) );337338 icedemo.ice_cfg.af = pj_AF_INET();339340/* Create DNS resolver if nameserver is set */341if (icedemo.opt.ns.slen)342 {343 CHECK( pj_dns_resolver_create(&icedemo.cp.factory,344"resolver",3450,346 icedemo.ice_cfg.stun_cfg.timer_heap,347 icedemo.ice_cfg.stun_cfg.ioqueue,348 &icedemo.ice_cfg.resolver) );349350 CHECK( pj_dns_resolver_set_ns(icedemo.ice_cfg.resolver, 1,351 &icedemo.opt.ns, NULL) );352 }353354/* -= Start initializing ICE stream transport config =- */355356/* Maximum number of host candidates */357if (icedemo.opt.max_host != -1)358 icedemo.ice_cfg.stun.max_host_cands = icedemo.opt.max_host;359360/* Nomination strategy */361if (icedemo.opt.regular)362 icedemo.ice_cfg.opt.aggressive = PJ_FALSE;363else364 icedemo.ice_cfg.opt.aggressive = PJ_TRUE;365366/* ⼿动设置TURN服务信息 */367 icedemo.opt.turn_srv = pj_str("11.11.11.11:8888");368 icedemo.opt.stun_srv = icedemo.opt.turn_srv;369 icedemo.opt.turn_username = pj_str("test");370 icedemo.opt.turn_password = pj_str("test");371372/* Configure STUN/srflx candidate resolution */373if (icedemo.opt.stun_srv.slen)374 {375char *pos;376377/* Command line option may contain port number */378if ((pos=pj_strchr(&icedemo.opt.stun_srv, ':')) != NULL)379 {380 icedemo.ice_cfg.stun.server.ptr = icedemo.opt.stun_srv.ptr;381 icedemo.ice_cfg.stun.server.slen = (pos - icedemo.opt.stun_srv.ptr);382383 icedemo.ice_cfg.stun.port = (pj_uint16_t)atoi(pos+1);384 }385else386 {387 icedemo.ice_cfg.stun.server = icedemo.opt.stun_srv;388 icedemo.ice_cfg.stun.port = PJ_STUN_PORT;389 }390391/* For this demo app, configure longer STUN keep-alive time392 * so that it does't clutter the screen output.393*/394 icedemo.ice_cfg.stun.cfg.ka_interval = KA_INTERVAL;395 }396397/* Configure TURN candidate */398if (icedemo.opt.turn_srv.slen)399 {400char *pos;401402/* Command line option may contain port number */403if ((pos=pj_strchr(&icedemo.opt.turn_srv, ':')) != NULL)404 {405 icedemo.ice_cfg.turn.server.ptr = icedemo.opt.turn_srv.ptr;406 icedemo.ice_cfg.turn.server.slen = (pos - icedemo.opt.turn_srv.ptr);407408 icedemo.ice_cfg.turn.port = (pj_uint16_t)atoi(pos+1);409 }410else411 {412 icedemo.ice_cfg.turn.server = icedemo.opt.turn_srv;413 icedemo.ice_cfg.turn.port = PJ_STUN_PORT;414 }415416/* TURN credential */417 icedemo.ice_cfg.turn.auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;418 icedemo.ice_cfg.turn.auth_cred.data.static_ername = icedemo.opt.turn_username; 419 icedemo.ice_cfg.turn.auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN; 420 icedemo.ice_cfg.turn.auth_cred.data.static_cred.data = icedemo.opt.turn_password;421422/* Connection type to TURN server */423//使⽤TCP协议424 icedemo.opt.turn_tcp = PJ_TRUE;425if (icedemo.opt.turn_tcp)426 icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_TCP;427else428 icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_UDP;429430/* For this demo app, configure longer keep-alive time431 * so that it does't clutter the screen output.432*/433 icedemo.ice_cfg.turn.alloc_param.ka_interval = KA_INTERVAL;434 }435436/* -= That's it for now, initialization is complete =- */437return PJ_SUCCESS;438 }439440441/*442* Create ICE stream transport instance, invoked from the menu.443*/444static void icedemo_create_instance(void)445 {446 pj_ice_strans_cb icecb;447 pj_status_t status;448449if (icedemo.icest != NULL)450 {451 puts("ICE instance already created, destroy it first");452return;453 }454455/* init the callback */456 pj_bzero(&icecb, sizeof(icecb));457 icecb.on_rx_data = cb_on_rx_data;458 icecb.on_ice_complete = cb_on_ice_complete;459460/* create the instance */461 status = pj_ice_strans_create("icedemo", /* object name */462 &icedemo.ice_cfg, /* settings */463 p_cnt, /* comp_cnt */464 NULL, /* user data */465 &icecb, /* callback */466 &icedemo.icest) /* instance ptr */467 ;468if (status != PJ_SUCCESS)469 icedemo_perror("error creating ice", status);470else471 PJ_LOG(3,(THIS_FILE, "ICE instance successfully created"));472 }473474/* Utility to nullify parsed remote info */475static void reset_rem_info(void)476 {477 pj_bzero(&icedemo.rem, sizeof(icedemo.rem));478 }479480481/*482* Destroy ICE stream transport instance, invoked from the menu.483*/484static void icedemo_destroy_instance(void)485 {486if (icedemo.icest == NULL)487 {488 PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first")); 489return;490 }491492 pj_ice_strans_destroy(icedemo.icest);493 icedemo.icest = NULL;494495 reset_rem_info();496497 PJ_LOG(3,(THIS_FILE, "ICE instance destroyed"));498 }499500501/*502* Create ICE session, invoked from the menu.503*/504static void icedemo_init_session(unsigned rolechar)505 {506 pj_ice_sess_role role = (pj_tolower((pj_uint8_t)rolechar)=='o' ?507 PJ_ICE_SESS_ROLE_CONTROLLING :508 PJ_ICE_SESS_ROLE_CONTROLLED);509 pj_status_t status;510511if (icedemo.icest == NULL)512 {513 PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first")); 514return;515 }516517if (pj_ice_strans_has_sess(icedemo.icest))518 {519 PJ_LOG(1,(THIS_FILE, "Error: Session already created"));520return;521 }522523 status = pj_ice_strans_init_ice(icedemo.icest, role, NULL, NULL); 524if (status != PJ_SUCCESS)525 icedemo_perror("error creating session", status);526else527 PJ_LOG(3,(THIS_FILE, "ICE session created"));528529 reset_rem_info();530 }531532533/*534* Stop/destroy ICE session, invoked from the menu.535*/536static void icedemo_stop_session(void)537 {538 pj_status_t status;539540if (icedemo.icest == NULL)541 {542 PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first")); 543return;544 }545546if (!pj_ice_strans_has_sess(icedemo.icest))547 {548 PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first")); 549return;550 }551552 status = pj_ice_strans_stop_ice(icedemo.icest);553if (status != PJ_SUCCESS)554 icedemo_perror("error stopping session", status);555else556 PJ_LOG(3,(THIS_FILE, "ICE session stopped"));557558 reset_rem_info();559 }560561#define PRINT(...) \562 printed = pj_ansi_snprintf(p, maxlen - (p-buffer), \563 __VA_ARGS__); \564if (printed <= 0 || printed >= (int)(maxlen - (p-buffer))) \565return -PJ_ETOOSMALL; \566 p += printed567568569/* Utility to create a=candidate SDP attribute */570static int print_cand(char buffer[], unsigned maxlen,571const pj_ice_sess_cand *cand)572 {573char ipaddr[PJ_INET6_ADDRSTRLEN];574char *p = buffer;575int printed;576577 PRINT("a=candidate:%.*s %u UDP %u %s %u typ ",578 (int)cand->foundation.slen,579 cand->foundation.ptr,580 (unsigned)cand->comp_id,581 cand->prio,582 pj_sockaddr_print(&cand->addr, ipaddr,583sizeof(ipaddr), 0),584 (unsigned)pj_sockaddr_get_port(&cand->addr));585586 PRINT("%s\n",587 pj_ice_get_cand_type_name(cand->type));588589if (p == buffer+maxlen)590return -PJ_ETOOSMALL;591592 *p = '\0';593594return (int)(p-buffer);595 }596597/*598* Encode ICE information in SDP.599*/600static int encode_session(char buffer[], unsigned maxlen)601 {602char *p = buffer;603 unsigned comp;604int printed;605 pj_str_t local_ufrag, local_pwd;606 pj_status_t status;607608/* Write "dummy" SDP v=, o=, s=, and t= lines */609 PRINT("v=0\no=- 3414953978 3414953978 IN IP4 localhost\ns=ice\nt=0 0\n"); 610611/* Get ufrag and pwd from current session */612 pj_ice_strans_get_ufrag_pwd(icedemo.icest, &local_ufrag, &local_pwd,613 NULL, NULL);614615/* Write the a=ice-ufrag and a=ice-pwd attributes */616 PRINT("a=ice-ufrag:%.*s\na=ice-pwd:%.*s\n",617 (int)local_ufrag.slen,618 local_ufrag.ptr,619 (int)local_pwd.slen,620 local_pwd.ptr);621622/* Write each component */623for (comp=0; comp<p_cnt; ++comp)624 {625 unsigned j, cand_cnt;626 pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND];627char ipaddr[PJ_INET6_ADDRSTRLEN];628629/* Get default candidate for the component */630 status = pj_ice_strans_get_def_cand(icedemo.icest, comp+1, &cand[0]); 631if (status != PJ_SUCCESS)632return -status;633634/* Write the default address */635if (comp==0)636 {637/* For component 1, default address is in m= and c= lines */638 PRINT("m=audio %d RTP/AVP 0\n"639"c=IN IP4 %s\n",640 (int)pj_sockaddr_get_port(&cand[0].addr),641 pj_sockaddr_print(&cand[0].addr, ipaddr,642sizeof(ipaddr), 0));643 }644else if (comp==1)645 {646/* For component 2, default address is in a=rtcp line */647 PRINT("a=rtcp:%d IN IP4 %s\n",648 (int)pj_sockaddr_get_port(&cand[0].addr),649 pj_sockaddr_print(&cand[0].addr, ipaddr,650sizeof(ipaddr), 0));651 }652else653 {654/* For other components, we'll just invent this.. */655 PRINT("a=Xice-defcand:%d IN IP4 %s\n",656 (int)pj_sockaddr_get_port(&cand[0].addr),657 pj_sockaddr_print(&cand[0].addr, ipaddr,658sizeof(ipaddr), 0));659 }660661/* Enumerate all candidates for this component */662 cand_cnt = PJ_ARRAY_SIZE(cand);663 status = pj_ice_strans_enum_cands(icedemo.icest, comp+1,664 &cand_cnt, cand);665if (status != PJ_SUCCESS)666return -status;667668/* And encode the candidates as SDP */669for (j=0; j<cand_cnt; ++j)670 {671 printed = print_cand(p, maxlen - (unsigned)(p-buffer), &cand[j]);672if (printed < 0)673return -PJ_ETOOSMALL;674 p += printed;675 }676 }677678if (p == buffer+maxlen)679return -PJ_ETOOSMALL;680681 *p = '\0';682return (int)(p - buffer);683 }684685686/*687* Show information contained in the ICE stream transport. This is688* invoked from the menu.689*/690static void icedemo_show_ice(void)691 {692static char buffer[1000];693int len;694695if (icedemo.icest == NULL)696 {697 PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));698return;699 }700701 puts("General info");702 puts("---------------");703 printf("Component count : %d\n", p_cnt);704 printf("Status : ");705if (pj_ice_strans_sess_is_complete(icedemo.icest))706 puts("negotiation complete");707else if (pj_ice_strans_sess_is_running(icedemo.icest))708 puts("negotiation is in progress");709else if (pj_ice_strans_has_sess(icedemo.icest))710 puts("session ready");711else712 puts("session not created");713714if (!pj_ice_strans_has_sess(icedemo.icest))715 {716 puts("Create the session first to see more info");717return;718 }719720 printf("Negotiated comp_cnt: %d\n",721 pj_ice_strans_get_running_comp_cnt(icedemo.icest));722 printf("Role : %s\n",723 pj_ice_strans_get_role(icedemo.icest)==PJ_ICE_SESS_ROLE_CONTROLLED ? 724"controlled" : "controlling");725726 len = encode_session(buffer, sizeof(buffer));727if (len < 0)728 err_exit("not enough buffer to show ICE status", -len);729730 puts("");731 printf("Local SDP (paste this to remote host):\n"732"--------------------------------------\n"733"%s\n", buffer);734735736 puts("");737 puts("Remote info:\n"738"----------------------");739if (icedemo.rem.cand_cnt==0)740 {741 puts("No remote info yet");742 }743else744 {745 unsigned i;746747 printf("Remote ufrag : %s\n", icedemo.rem.ufrag);748 printf("Remote password : %s\n", icedemo.rem.pwd);749 printf("Remote cand. cnt. : %d\n", icedemo.rem.cand_cnt);750751for (i=0; i<icedemo.rem.cand_cnt; ++i)752 {753 len = print_cand(buffer, sizeof(buffer), &icedemo.rem.cand[i]);754if (len < 0)755 err_exit("not enough buffer to show ICE status", -len);756757 printf(" %s", buffer);758 }759 }760 }761762763/*764* Input and parse SDP from the remote (containing remote's ICE information) 765* and save it to global variables.766*/767static void icedemo_input_remote(void)768 {769char linebuf[80];770 unsigned media_cnt = 0;771 unsigned comp0_port = 0;772char comp0_addr[80];773 pj_bool_t done = PJ_FALSE;774775 puts("Paste SDP from remote host, end with empty line");776777 reset_rem_info();778779 comp0_addr[0] = '\0';780781while (!done)782 {783 pj_size_t len;784char *line;785786 printf(">");787if (stdout) fflush(stdout);788789if (fgets(linebuf, sizeof(linebuf), stdin)==NULL)790break;791792 len = strlen(linebuf);793while (len && (linebuf[len-1] == '\r' || linebuf[len-1] == '\n'))794 linebuf[--len] = '\0';795796 line = linebuf;797while (len && pj_isspace(*line))798 ++line, --len;799800if (len==0)801break;802803/* Ignore subsequent media descriptors */804if (media_cnt > 1)805continue;806807switch (line[0])808 {809case'm':810811 {812int cnt;813char media[32], portstr[32];814815 ++media_cnt;818 puts("Media line ignored");819break;820 }821822 cnt = sscanf(line+2, "%s %s RTP/", media, portstr);823if (cnt != 2)824 {825 PJ_LOG(1,(THIS_FILE, "Error parsing media line"));826goto on_error;827 }828829 comp0_port = atoi(portstr);830831 }832break;833case'c':834835 {836int cnt;837char c[32], net[32], ip[80];838839 cnt = sscanf(line+2, "%s %s %s", c, net, ip);840if (cnt != 3)841 {842 PJ_LOG(1,(THIS_FILE, "Error parsing connection line"));843goto on_error;844 }845846 strcpy(comp0_addr, ip);847 }848break;849case'a':850851 {852char *attr = strtok(line+2, ": \t\r\n");853if (strcmp(attr, "ice-ufrag")==0)854 {855 strcpy(icedemo.rem.ufrag, attr+strlen(attr)+1);856 }857else if (strcmp(attr, "ice-pwd")==0)858 {859 strcpy(icedemo.rem.pwd, attr+strlen(attr)+1);860 }861else if (strcmp(attr, "rtcp")==0)862 {863char *val = attr+strlen(attr)+1;864int af, cnt;865int port;866char net[32], ip[64];867 pj_str_t tmp_addr;868 pj_status_t status;869870 cnt = sscanf(val, "%d IN %s %s", &port, net, ip);871if (cnt != 3)872 {873 PJ_LOG(1,(THIS_FILE, "Error parsing rtcp attribute"));874goto on_error;875 }876877if (strchr(ip, ':'))878 af = pj_AF_INET6();879else880 af = pj_AF_INET();881882 pj_sockaddr_init(af, &icedemo.rem.def_addr[1], NULL, 0);883 tmp_addr = pj_str(ip);884 status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[1], 885 &tmp_addr);886if (status != PJ_SUCCESS)887 {888 PJ_LOG(1,(THIS_FILE, "Invalid IP address"));889goto on_error;890 }891 pj_sockaddr_set_port(&icedemo.rem.def_addr[1], (pj_uint16_t)port); 892893 }894else if (strcmp(attr, "candidate")==0)895 {896char *sdpcand = attr+strlen(attr)+1;897int af, cnt;898char foundation[32], transport[12], ipaddr[80], type[32];899 pj_str_t tmpaddr;。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
PJSIP
基于一个开放的、成熟的SIP开源库进行开发不但可以大大提高效率,也可增强与其他的SIP系统的兼容性。
PJSIP是用C编写的,相当优秀的一个SIP协议栈,其主要特征包括:
1.极具移植性。
支持的平台有Windows、Windows Mobile、Linux、Unix、MacOS X、
RTEMS、Symbian OS等。
2.非常小的存储空间。
包含完整SIP功能的代码库仅150K。
3.高性能。
采用优秀的内存分配机制,运行速度快。
4.支持众多SIP特征和扩展。
比如IM、presence、event subscription、call transfer、
PIDF等。
5.丰富的SIP文档和范例。
PJSIP 开源库由一系列功能库所组成,如图1所示,PJLIB 是系统抽象层,PJLIB-UTIL 提供有用的工具函数,PJNATH 解决NAT 穿越问题,PJMEDIA 和PJMEDIA-CODEC 负责SDP 协商、媒体编码和媒体传输,PJSIP 是核心SIP 协议栈,PJSIP-SIMPLE 实现Presence 和即时消息,PJSIP-UA 提供SIP 用户代理库,PJSUA 位于最高层,整合了下层模块的全部功能。
PJSIP 的每个功能库根据其所在的层次以及负责的功能都提供了丰富的编程接口,方便开发人员使用。
PJSIP 协议栈内部包含多个SIP 消息处理层,如下图2所示,从下往上依次TRANSPORT 层、ENDPOINT 层、TRANSACTION 层、UA 层和DIALOG 层。
每个消息处理层以模块的形式注册到协议栈中,开发者也可以编写并添加自己的消息处理模块,对SIP 消息进行解析或修改。
当TRANSPORT MANAGER 收到SIP 消息包时,会把该SIP EVENT 通知上层的ENDPOINT,而ENDPOINT 会找到对应的接收者,先把EVENT 传给TRANSACTION LAYER,然后再传给UA LAYER(传递的顺序由每个模块的优先权决定),如果UA LAYER
指定要处理TRANSACTION 的EVENT,TRANSACTION LAYER 也会把解析后的EVENT 传给UA LAYER。