【Android源码 栏目提醒】:网学会员在Android源码 频道为大家收集整理了“基于Android的VoIP系统 - 企业软件开发“提供大家参考,希望对大家有所帮助!
2011年3月15日第34卷第6期现代电子技术ModernElectronicsTechniqueMar.2011Vol.34No.6基于
Android的VoIP系统贺丹丹施展上海理工大学光电信息与计算机工程学院上海200090摘要:为实现一个
Android平台的网络电话提出一个基于PJSIP堆栈的VoIP系统设计方案。
该方案符合AndroidNDK开发规范即上层使用Java语言开发下层采用纯C语言的PJSIP库开发其协议栈并使用JNI连接上下两部分。
在此叙述了PJSIP的原理并以此为出发点分析并设计了一个具体的VoIP系统。
最后通过测试该方案能很好地完成会话的发起、应答、通信以及会话的注销等功能达到了设计要求。
关键词:
Android网络电话PJSIPJNINDK中图分类号:TN91134文献标识码:A文章编号:1004373X201106002804DesignandRealizationofVoIPBasedonAndroidOSHEDandanSHIZhanSchoolofOpticalelectricalandComputerEngineeringUniversityofShanghaiforScienceandTechnologyShanghai200090ChinaAbstract:TorealizeaVoIPforAndroidplatformadesignschemeofVoIPsystembasedonPJSIPprotocolstackisproposed.ThesystemmeetstheAndroidNDKdevelopmentspecificationswhichisdevelopedbyJavalanguageinapplicationlayeranddevelopedbyCinAndroidframeworklayer.ThePJSIPprincipleisdescribedandasastartingpointtodesignaspecificVoIPsystem.Thesystemcanmeetthedesignrequirementsthroughatest.Keywords:AndroidVoIPPJSIPJNINDK收稿日期:20101029基金项目:国家自然科学基金资助项目605731420引言VoIPVoiceoverInternetProtocol即首先数字化语音信号并压缩成帧转换为IP数据包在网络上传输以此完成语音通话的业务是一种利用IP协议传输语音数据的、新兴的通信技术1。
随着我国三网融合的推进VoIP与IPTVInteractivePersonalityTV一起成为这一庞大工程的重要标志。
而目前手机中VoIP的解决方案并不是很多特别是在Google公司推出的开源操作系统
Android中。
尽管该系统推出时间不长凭借强大的功能、良好的界面、广泛的商业支持为用户带来很好的体验成为2010年最热门且发展最快的手机操作系统。
因此两者的结合将是未来的发展趋势。
本文提出一种基于PJSIP协议栈的解决方案通过
Android本地开发工具NDK实现一个高效、稳定且功能强大的VoIP系统具有较高的参考和实用价值。
1VoIP设计方案1.1设计目标本方案所设计的系统包含以下功能:首先完成用户终端如手机中语音数据的采集与编码并通过RTP实时传输协议/RTCPRTP传输控制协议进行传输和控制其次完成会话的控制包括会话的注册、发起、维护与结束、注销等再次作为一个应用程序必须实现一个良好的界面与用户交互最后作为一个开放系统需具有良好的可扩展性。
1.2总体设计本方案基本上符合
Android的NDK框架的开发规范将系统分为4层如图1所示。
最上层为应用层该层将在AndroidSDK的框架内采用Java语言来实现第二层为JNI层SIP协议栈有很多种实现其中采用C语言的SIP协议栈在效率、速度、系统占用方面有着超越其他库如Java协议栈的优势因此该方案将在第三层采用纯C语言实现的PJSIP协议栈。
为了让Java应用层能调用协议栈层在两层之间需要一个衔接的桥梁这就是JNI层。
最后一层是驱动层这部分一般是由手机厂商来实现的本文将不做重点介绍。
2VoIP的具体实现这里将实现一个完整的VoIP系统包括协议栈的实现、JNI的编写以及上层UI的设计实现等。
2.1SIP协议栈及UASIP协议栈直接关系到整个系统的质量与效率本文将采用纯C语言开发的PJSIP库。
该库采用C语言开发且
源码开放在兼容性与效率上有明显优势不仅体积小完整的SIP封装也不过150KB2同时还实现了一个内存池使得安全系数与运行效率大为提高该系统所采用的就是优化后的PJSIP库。
图1总体设计框架图2.1.1PJSIP协议栈PJSIP协议栈遵循标准的SIP协议采用分层架构:SIP/SDP消息编码解析层、传输管理层、SIP终端、事务层、会话层以及应用层等3。
由于SIP协议采用文本消息发送请求和响应所以首先需要将SIP消息按照巴斯克范式ABNF编码和解析这就是SIP/SDP消息编码解析层所完成的功能。
传输管理层用来管理用户代理与服务器之间的请求和相应SIP终端是PJSIP中转机制的实现它主要负责管理各个SIP组建例如像SIP终端实例注册组件分发消息到事务层、会话层及应用层回传处理结果管理定时器、I/O队列等事务层通过状态机机制管理SIP信令每一次状态机状态的改变都将触发回调函数45会话层负责会话的发起与响应一般与应用层结合在一起用于用户交互不同的平台有不同的实现本文使用Andriod的GUI来实现。
PJSIP是一个高度封装的库实际上它是通过PJSUA子库来实现应用的。
一个完整的PJSUA生命周期首先需要初始化通过函数init来实现。
在这个函数中将创建代理、初始化变量和堆栈以及创建一个UDP传输并在最后启动代理第二步将为UA添加用户如果需要的话还要向服务器注册用户当用户添加成功后此时可以建立一个呼叫连接发起会话当会话连接成功后就可以使用SRTP协议实时传输加密后的数据进行通话。
最后的过程是挂起或销毁呼叫。
2.1.2UA原理UAUserAgency是协议栈的具体实现PJSIP通过封装了的PJSUA来实现在这一点上大部分的SIP库都大同小异在此将介绍UA的工作原理。
一个典型的UA包含UACUserAgencyClient和UASUserAgencyServer两部分。
会话由UAC发起。
当呼叫发起时UAC将首先发送INVITE消息给SIP代理服务器服务器收到INVITE消息后将返回一个应答200OK并回答ACK进行确认同时通知主叫用户即会话发起用户上线通话。
如果主叫端用户端主动结束会话UAC将返回BYE消息同时通知服务器如果用户端收到服务器传来的BYE消息回答200并结束会话6。
服务器端UAS收到UAC用户端发来的INVITE消息首先从消息中提取出主、被叫对象然后检查当前是否有空闲信道若没有则返回486BUSYHERE即系统忙消息接着将检查被叫用户是否在服务区如果被叫对象不在服务范围则返回404NOTFOUND即用户不在服务区若被叫用户成功上线则返回200OK同时准备开始会话。
SIP协议栈一般使用SIP统一资源定位符URL来标识它根据URL来寻址如集群用户200300分别对应SIP用户为200192.168.1.100300192.168.1.100。
本文中也使用这种方式来测试通信7。
2.2JNI的实现PJSIP库和Java类连接是通过JNI来实现的这也是AndroidNDK的实现机制JNI是SUN公司推出的用于Java调用其他语言的接口。
首先需要一个中间类这个类中主要建立一些方法用于调用C/C本地函数。
它们的类型均为publicstaticnativeint以便其他的Java类能够调用。
2.2.1新建PJSIP类为各个待实现的类新建一个包可以命名为com.
android.voip.pjsip在该包中添加该系统相关的一些类主要有如下6个类:publicstaticnativeintinitStringproxypublicstaticnativeintadd_accountStringsip_userStringsip_domainStringsip_passwdpublicstaticnativeintacc_get_defaultpublicstaticnativeintmake_callintacc_idStringuripublicstaticnativevoidhanguppublicstaticnativevoiddestroy这些类分别为上节中原理各个步骤的实现。
这部分仅仅是为C库的调用提供一个接口因此具体的实现将放在本地C/C程序中。
2.2.2头文件的生成C库与Java间还需通过一个后缀为.h的头文件来衔接这个头文件可以手动编写也可以通过Javah来生成该工具包含在JDK中是由SUN公司提供的。
Javah生产的头文件包含一定的规则例如本例中它将生成的函数声明为Java_com_
android_IMSandroid_pjsip_的形式以便在调用C库时能正确识别。
29第6期贺丹丹等:基于
Android的VoIP系统由于Java中的数据类型与C/C不尽相同因此还必须注意参数传递时参数类型的转换。
本文所涉及到的Java数据类型有String和int型Javah生成的头文件中会先定义好需要传递的参数类型以及函数返回类型例如方法add_accountStringsip_userStringsip_domainStringsip_passwd在头文件中将定义函数的形式为JNIEXPORTjintJNICALLJava_com_
android_IMSandroid_pjsip_add_1accountJNIEnvjclassjstringjstringjstring其中JNIEXPORT为JNI外部函数声明jint是jni.h中定义C语言中整形的对应类型JNICALL是JNI关键字。
比较特殊的是JNIEnv它是一个指向类型为JNIEnv_的一个特殊JNI数据结构的指针它的每个元素都指向一个JNI函数的指针jclass会根据引用Java类的不同而不同本文中pjsip.class是静态类因此这里的jclass指的是类本身如果是非静态类则指的是对象8。
后面几个就是pjsip类需要传递的参数根据jni.h的定义String类型对应jstringint对应jint。
然而这只是函数申明与类中方法的形式对应参数的具体传递还需要相应的转化具体实现将在下一节详细介绍。
2.2.3JNI接口函数的实现创建了pjsip库类和头文件之后必须应用一个库接口函数这部分是pjsip接口的实现限于篇幅本文只讲解几个重要函数的实现。
1init函数首先是init函数对应的接口函数为JNICALLJava_com_
android_IMSandroid_pjsip_init。
该函数在系统初始化时调用其作用是配置相关参数并发起一个pjsua应用。
它先通过函数pjsua_create创建一个pjsua应用然后通过三个函数pjsua_config_defaultcfgpjsua_logging_config_defaultlog_cfgpjsua_media_config_defaultmedia_cfg配置其相关参数其中cfg是pjsua的相关参数主要是状态改变时的回调函数log_cfg用来配置log级别media_cfg中包含时钟频率、声道数目等相关参数。
完成配置之后就可以使用pjsua_initcfglog_cfgmedia_cfg将先前配置的参数初始化。
在初始化之后还需为pjsua添加一个udp传输这一步是通过pjsua_transport_createPJSIP_TRANSPORT_UDPcfgNULL来实现的cfg中包含指定的通讯端口3GPP建议使用5060.。
需要注意的是配置完以上参数之后还需指定SPEEX编码优先级一般将其设为最大可以通过函数pjsua_codec_set_priorityspeex_codec_id255来实现。
所有配置完成之后就可以发起pjsua即最后调用pjsua_start。
成功的话本函数的返回类型为PJ_SUCCESS。
2make_call函数另一个很重要的函数是make_call其在本接口文件中对应的函数为Java_com_
android_IMSandroid_pjsip_make_1call这个函数一般在发起会话时调用它与上一个函数在结构上最大的不同在于本函数需要传递一个字符串参数前面提到Java与C/C在参数结构上并不完全相同因此这里需要将Java传递过来的String类型的参数转化可以通过url_ptrcharenvGetStringUTFCharsurliscopy来实现。
envGetStringUTFChars在jni.h中定义其功能是将jsting类型Java的url复制到char类型C的url_ptr中以此来完成参数类型的转换。
为了保证传递地址的有效性还需要使用pjsua_verify_sip_urlurl_ptr验证这个函数主要验证url_ptr是符合SIP的规则即是否是一个合法的SIP地址。
然而char型的地址pjsua中还是不能直接使用的这是因为pjsua重新封装了参数类型所以最后还需要将其转化成pj_str_t类型pjlib提供pj_str函数可以完成转化。
在完成了参数的转化后调用pjsua_call_make_call将发起会话。
3hangup函数和pjsua_destroy函数这两个函数用来销毁和挂断会话一般在需结束的时候调用它们在接口函数中对应Java_com_
android_IMSandroid_pjsip_hangup和Java_com_
android_IMSandroid_pjsip_destroy由于没有参数传递也没有其他的调用因此这两个函数非常简单基本上直接调用pjsua提供的pjsua_call_hangup_all和pjsua_destroy就能实现。
pjsua中这两个函数将完成内存释放、账户注销等工作。
4add_account函数该函数在基本的pjsua中并不是必须的但如果要使用SIP服务器的话就必须实现该函数它在接口函数中对应Java_com_
android_IMSandroid_pjsip_add_1account同make_call一样也需要传递参数不同的是它传递三个参数只是原理大体一样。
首先它将参数转化后保持到cfg通过pjsua_acc_addcfgPJ_TRUEacc_id将参数添加到pjsua。
pjsua将以其中的sip服务器为目的地址注册会话发起申请经过一系列的操作之后与目的地址发起会话。
2.2.4主程序与用户界面系统的主程序是一个标准的
Android应用程序它使用Java语言开发符合SDK规范。
与一般的SDK30现代电子技术2011年第34卷程序不同的是该类中必须使用System.loadLibrary加载PJSIP库文件9。
形式如下:System.loadLibrarypjsipjni其中pjsipjni就是上节中PJSIP协议栈生成的库。
主程序中的基本方式均按照上节中的过程创建并初始化PJSUA当call按键被触发时发起会话调用make_call方法当用户接受通话时点击hang或cancel按键触发hang或采用destry方法等。
用户接口是通过AndroidSDK来实现的这部分几乎全都是Java语言由于UI不是本文的重点因此只介绍一个简单的界面实际应用中用户交互是非常重要的。
为了实现所需的功能至少需要一个文本框来提供SIP地址以及两个按键来控制会话发起和结束。
另外在呼叫与通话过程中还需要一个页面来显示这里可以通过对话框来显示最后的界面如图2所示。
3封装与调试为了能在
Android平台上方便地使用该系统在实现了PJSIP协议栈、JNI接口以及UI之后还需将上面所有的模块进行封装。
AndroidSDK提供了一些很有用的工具如aapt等由于本文重点不在AndriodSDK所以可以采用集成开发工具如集成在Eclipse中的ADT来封装。
在工程libs如果不存在则新建目录下新建一个名为armeabi的目录将上节生成的.so库文件放到该目录下。
ADT在封装资源时会自动将该库文件封装到apk文件中apk是
Android操作系统中应用程序的封装形式在所有
android平台中均能使用10。
封装后安装到
Android手机、MID或虚拟机中并发起会话。
与开源SIP软件Linphone通信的结果如图2所示。
4结语通过测试表明该系统能够对发起并很好地控制SIP信令该系统由于采用SIP协议因此与所有采用这一协议的软件均能通信如LinphoneKphone等功能测试中表现良好实现了VoIP的基本需求。
同时如果要增加功能可以在Java类中添加相应的方法并在应用层调用即可具有一定的可扩展性。
图2软件界面由于手机等手持设备在规格和配置上的差异该系统在具体的设备上使用时界面略有不同但是同系统架构的手机使用时并不影响功能在HTCDesire和MOTOMilestone上测试均能正常使用。
但是当移植到不同的架构时即使同时ARM架构仍需做一定的优化一般采取主流平台的多种版本方式来解决这也是所有多厂商移动设备上一个无法避免的问题。
参考文献1DANIELCollins.VoIP技术与应用M.北京:人民邮电出版社2003.2刘洋侯红.基于SIP协议的IP电话技术J.计算机技术与发展2006164:184189.3ROSENBERGJSSHULARINNEHCAMARILLOGetal.RFC3261SIP:sessioninitiationprotocolS.S.l.:TheInternetSociety2002.4魏立峰.基于嵌入式系统BF561的PJSIP协议栈移植J.计算机工程与设计20093017:39463947.5BennyPrijono.PJSIP协议栈开发主页EB/OL.20101020.http://www.pjsip.org.6赵强张成文左荣国.基于软交换的NGN技术与应用开发实例M.北京:人民邮电出版社2009.7李静林孙其博杨放春.下一代网络通信协议分析M.北京:人民邮电出版社2010.8任俊伟林东岱.JNI技术实现跨平台开发的研究J.计算机应用研究2005227:180184.9彭波孙一林.Java多媒体技术M.北京:清华大学出版社2005.10Google.
Android开发主页EB/OL.20101020.http://www.
android.com.作者简介:贺丹丹男1984年出生湖南衡阳人硕士研究生。
主要研究方向为嵌入式及其应用、
Android操作系统。
施展女1963年出生浙江余姚人副教授。
主要研究方向为精密测试技术及仪器。
31第6期贺丹丹等:基于
Android的VoIP系统
上一篇:
基于Android的智能家居系统的研究与实现
下一篇:
asp论文:浅论ASP在多媒体网页课件制作中的应用