• <strike id="fdgpu"><input id="fdgpu"></input></strike>
    <label id="fdgpu"></label>
    <s id="fdgpu"><code id="fdgpu"></code></s>

  • <label id="fdgpu"></label>
  • <span id="fdgpu"><u id="fdgpu"></u></span>

    <s id="fdgpu"><sub id="fdgpu"></sub></s>
     首頁(yè) > 技術(shù) > 技術(shù)文摘 > 基于A(yíng)ndroid的VoIP系統實(shí)現原理

    基于A(yíng)ndroid的VoIP系統實(shí)現原理

    2011-05-05 00:00:00   作者:   來(lái)源:   評論:0 點(diǎn)擊:



      VoIP(Voice over Internet Protocol)即首先數字化語(yǔ)音信號并壓縮成幀,轉換為IP數據包在網(wǎng)絡(luò )上傳輸,以此完成語(yǔ)音通話(huà)的業(yè)務(wù),是一種利用IP協(xié)議傳輸語(yǔ)音數據的、新興的通信技術(shù)。
      隨著(zhù)我國三網(wǎng)融合的推進(jìn),VoIP與IPTV(Interactive Personality TV)一起成為這一龐大工程的重要標志。而目前手機中,VoIP的解決方案并不是很多,特別是在Google公司推出的開(kāi)源操作系統Android中。盡管該系統推出時(shí)間不長(cháng),憑借強大的功能、良好的界面、廣泛的商業(yè)支持,為用戶(hù)帶來(lái)很好的體驗,成為2010年最熱門(mén)且發(fā)展最快的手機操作系統。因此,兩者的結合,將是未來(lái)的發(fā)展趨勢。本文提出一種基于PJSIP協(xié)議棧的解決方案,通過(guò)Android本地開(kāi)發(fā)工具(NDK),實(shí)現一個(gè)高效、穩定且功能強大的VoIP系統,具有較高的參考和實(shí)用價(jià)值。
      1 VoIP設計方案
      1.1 設計目標

      本方案所設計的系統包含以下功能:首先,完成用戶(hù)終端(如手機)中語(yǔ)音數據的采集與編碼,并通過(guò)RTP(實(shí)時(shí)傳輸協(xié)議)/RTCP(RTP傳輸控制協(xié)議)進(jìn)行傳輸和控制;其次,完成會(huì )話(huà)的控制,包括會(huì )話(huà)的注冊、發(fā)起、維護與結束、注銷(xiāo)等;再次,作為一個(gè)應用程序,必須實(shí)現一個(gè)良好的界面,與用戶(hù)交互;最后,作為一個(gè)開(kāi)放系統,需具有良好的可擴展性。
      1.2 總體設計
      本方案基本上符合Android的NDK框架的開(kāi)發(fā)規范,將系統分為4層,如圖1所示。最上層為應用層,該層將在A(yíng)ndroid SDK的框架內,采用Java語(yǔ)言來(lái)實(shí)現;第二層為JNI層,SIP協(xié)議棧有很多種實(shí)現,其中,采用C語(yǔ)言的SIP協(xié)議棧在效率、速度、系統占用方面有著(zhù)超越其他庫(如Java協(xié)議棧)的優(yōu)勢,因此,該方案將在第三層采用純C語(yǔ)言實(shí)現的PJSIP協(xié)議棧。為了讓Java應用層能調用協(xié)議棧層,在兩層之間需要一個(gè)銜接的橋梁,這就是JNI層。最后一層是驅動(dòng)層,這部分一般是由手機廠(chǎng)商來(lái)實(shí)現的,本文將不做重點(diǎn)介紹。
      2 VoIP的具體實(shí)現
      這里將實(shí)現一個(gè)完整的VoIP系統,包括協(xié)議棧的實(shí)現、JNI的編寫(xiě)以及上層UI的設計實(shí)現等。
      2.1 SIP協(xié)議棧及UA
      SIP協(xié)議棧直接關(guān)系到整個(gè)系統的質(zhì)量與效率,本文將采用純C語(yǔ)言開(kāi)發(fā)的PJSIP庫。該庫采用C語(yǔ)言開(kāi)發(fā),且源碼開(kāi)放,在兼容性與效率上有明顯優(yōu)勢,不僅體積小(完整的SIP封裝也不過(guò)150 KB),同時(shí)還實(shí)現了一個(gè)內存池,使得安全系數與運行效率大為提高,該系統所采用的就是優(yōu)化后的PJSIP庫。
      2.1.1 PJSIP協(xié)議棧
      PJSIP協(xié)議棧遵循標準的SIP協(xié)議,采用分層架構:SIP/SDP消息編碼解析層、傳輸管理層、SIP終端、事務(wù)層、會(huì )話(huà)層以及應用層等。由于SIP協(xié)議采用文本消息發(fā)送請求和響應,所以首先需要將SIP消息按照巴斯克范式(ABNF)編碼和解析,這就是SIP/SDP消息編碼解析層所完成的功能。傳輸管理層用來(lái)管理用戶(hù)代理與服務(wù)器之間的請求和相應;SIP終端是PJSIP中轉機制的實(shí)現,它主要負責管理各個(gè)SIP組建,例如像SIP終端實(shí)例注冊組件,分發(fā)消息到事務(wù)層、會(huì )話(huà)層及應用層,回傳處理結果,管理定時(shí)器、I/O隊列等;事務(wù)層通過(guò)狀態(tài)機機制管理SIP信令,每一次狀態(tài)機狀態(tài)的改變都將觸發(fā)回調函數;會(huì )話(huà)層負責會(huì )話(huà)的發(fā)起與響應,一般與應用層結合在一起,用于用戶(hù)交互,不同的平臺有不同的實(shí)現,本文使用Andriod的GUI來(lái)實(shí)現。
      PJSIP是一個(gè)高度封裝的庫,實(shí)際上它是通過(guò)PJSUA子庫來(lái)實(shí)現應用的。一個(gè)完整的PJSUA生命周期,首先需要初始化,通過(guò)函數init()來(lái)實(shí)現。在這個(gè)函數中,將創(chuàng )建代理、初始化變量和堆棧,以及創(chuàng )建一個(gè)UDP傳輸并在最后啟動(dòng)代理;第二步將為UA添加用戶(hù),如果需要的話(huà),還要向服務(wù)器注冊用戶(hù);當用戶(hù)添加成功后,此時(shí)可以建立一個(gè)呼叫連接,發(fā)起會(huì )話(huà);當會(huì )話(huà)連接成功后,就可以使用SRTP協(xié)議實(shí)時(shí)傳輸加密后的數據,進(jìn)行通話(huà)。最后的過(guò)程是掛起或銷(xiāo)毀呼叫。
      2.1.2 UA原理
      UA(User Agency)是協(xié)議棧的具體實(shí)現,PJSIP通過(guò)封裝了的PJSUA來(lái)實(shí)現,在這一點(diǎn)上,大部分的SIP庫都大同小異,在此將介紹UA的工作原理。
      一個(gè)典型的UA包含UAC(User Agency Client)和UAS(User Agency Server)兩部分。會(huì )話(huà)由UAC發(fā)起。當呼叫發(fā)起時(shí),UAC將首先發(fā)送“IN-VITE”消息給SIP代理服務(wù)器,服務(wù)器收到“INVITE”消息后將返回一個(gè)應答“200 OK”,并回答“ACK”進(jìn)行確認,同時(shí)通知主叫用戶(hù)(即會(huì )話(huà)發(fā)起用戶(hù))上線(xiàn)通話(huà)。如果主叫端(用戶(hù)端)主動(dòng)結束會(huì )話(huà),UAC將返回“BYE”消息,同時(shí)通知服務(wù)器;如果用戶(hù)端收到服務(wù)器傳來(lái)的“BY-E”消息,回答“200”,并結束會(huì )話(huà)。
      服務(wù)器端,UAS收到UAC(用戶(hù)端)發(fā)來(lái)的“INVITE”消息,首先從消息中提取出主、被叫對象,然后檢查當前是否有空閑信道,若沒(méi)有則返回“486 BUSY HERE”(即系統忙)消息;接著(zhù)將檢查被叫用戶(hù)是否在服務(wù)區,如果被叫對象不在服務(wù)范圍,則返回“404 NOT FOUND”(即用戶(hù)不在服務(wù)區);若被叫用戶(hù)成功上線(xiàn),則返回“200 OK”,同時(shí)準備開(kāi)始會(huì )話(huà)。
      SIP協(xié)議棧一般使用SIP統一資源定位符(URL)來(lái)標識,它根據URL來(lái)尋址,如集群用戶(hù)“200”,“300”分別對應SIP用戶(hù)為“200@192.168. 1.100”,“300@192.168.1.100”。本文中也使用這種方式來(lái)測試通信。
      2.2 JNI的實(shí)現
      PJSIP庫和Java類(lèi)連接是通過(guò)JNI來(lái)實(shí)現的,這也是Android NDK的實(shí)現機制,JNI是SUN公司推出的用于Java調用其他語(yǔ)言的接口。
      首先需要一個(gè)中間類(lèi),這個(gè)類(lèi)中主要建立一些方法用于調用C/C++本地函數。它們的類(lèi)型均為“publicstatic native int”,以便其他的Java類(lèi)能夠調用。
      2.2.1 新建PJSIP類(lèi)
      為各個(gè)待實(shí)現的類(lèi)新建一個(gè)包,可以命名為“com.a(chǎn)ndroid.VoIP.pjsip”,在該包中添加該系統相關(guān)的一些類(lèi),主要有如下6個(gè)類(lèi):
      2.2.2 頭文件的生成
      C庫與Java間還需通過(guò)一個(gè)后綴為“.h”的頭文件來(lái)銜接,這個(gè)頭文件可以手動(dòng)編寫(xiě),也可以通過(guò)“Javah”來(lái)生成,該工具包含在JDK中,是由SUN公司提供的。
      Javah生產(chǎn)的頭文件包含一定的規則,例如,本例中,它將生成的函數聲明為“Java_com_android_IMSandroid_pjsip_**”的形式,以便在調用C庫時(shí)能正確識別。
      由于Java中的數據類(lèi)型與C/C++不盡相同,因此還必須注意參數傳遞時(shí)參數類(lèi)型的轉換。本文所涉及到的Java數據類(lèi)型有String和int型,Javah生成的頭文件中會(huì )先定義好需要傳遞的參數類(lèi)型以及函數返回類(lèi)型,例如方法“add_account(String sip_user,Stringsip_dom-ain,String sip_passwd)”,在頭文件中將定義函數的形式為“JNIEXPORT jint JNICALL Java_com_android_IMSandroid_pjsip_add_lac-count(JNIEnv*,jclass,jstring,jstring,jstring)”,其中JNIEXPORT為JNI外部函數聲明,jint是“jni.h”中定義C語(yǔ)言中整形的對應類(lèi)型,JNCALL是JNI關(guān)鍵字。比較特殊的是JNIEnv,它是一個(gè)指向類(lèi)型為JNIEnv_的一個(gè)特殊JNI數據結構的指針,它的每個(gè)元素都指向一個(gè)JNI函數的指針,jclass會(huì )根據引用Java類(lèi)的不同而不同,本文中“pjsip.class”是靜態(tài)類(lèi),因此這里的jclass指的是類(lèi)本身,如果是非靜態(tài)類(lèi)則指的是對象。后面幾個(gè)就是pjsip類(lèi)需要傳遞的參數,根據“jni.h”的定義,String類(lèi)型對應jstring,int對應jint。然而這只是函數申明與類(lèi)中方法的形式對應,參數的具體傳遞還需要相應的轉化,具體實(shí)現將在下一節詳細介紹。
      2.2.3 JNI接口函數的實(shí)現
      創(chuàng )建了pjsip庫類(lèi)和頭文件之后,必須應用一個(gè)庫接口函數,這部分是pjsip接口的實(shí)現,限于篇幅,本文只講解幾個(gè)重要函數的實(shí)現。
      (1)init函數
      首先是init函數,對應的接口函數為JNICALL Java_com_android_IMSandroid_pjsip_init。該函數在系統初始化時(shí)調用,其作用是配置相關(guān)參數,并發(fā)起一個(gè)pjsua應用。它先通過(guò)函數“pjsua_create()”創(chuàng )建一個(gè)“pjsua”應用,然后通過(guò)三個(gè)函數“pjsua_config_default(&cfg)”,“pjsua_logging_config_default(&log_cfg”),“pjsua_media_config_default(&media_cfg)”配置其相關(guān)參數,其中cfg是pjsua的相關(guān)參數,主要是狀態(tài)改變時(shí)的回調函數;log_cfg用來(lái)配置log級別;media_cfg中包含時(shí)鐘頻率、聲道數目等相關(guān)參數。
      完成配置之后就可以使用pjsua_init(&cfg,&log_cfg,&media_cfg)將先前配置的參數初始化。在初始化之后,還需為pjsua添加一個(gè)udp傳輸,這一步是通過(guò)pjsua_transport_create(PJSIP_TRANSPORT_UDP,&cfg,NULL)來(lái)實(shí)現的,cfg中包含指定的通訊端口,3GPP建議使用5060。
      需要注意的是,配置完以上參數之后,還需指定SPEEX編碼優(yōu)先級,一般將其設為最大,可以通過(guò)函數pisua_codec_set_priority(&-speex_codec_id,255)來(lái)實(shí)現。所有配置完成之后,就可以發(fā)起pjsua,即最后調用pjsua_start()。成功的話(huà),本函數的返回類(lèi)型為PJ_SUCCESS。
      (2)make_call函數
      另一個(gè)很重要的函數是make_call,其在本接口文件中對應的函數為Java_com_Android_IMSandroid_pjsip_make_lcall,這個(gè)函數一般在發(fā)起會(huì )話(huà)時(shí)調用,它與上一個(gè)函數在結構上最大的不同在于本函數需要傳遞一個(gè)字符串參數,前面提到,Java與C/C++在參數結構上并不完全相同,因此這里需要將Java傳遞過(guò)來(lái)的String類(lèi)型的參數轉化,可以通過(guò)“url_ptr=(char*)env-》GetStringUTFChars(url,&iscopy)”來(lái)實(shí)現。env-》GetStringUTFChars在“jni.h”中定義,其功能是將jsting類(lèi)型(Java)的url復制到char*類(lèi)型(C)的url_ptr中,以此來(lái)完成參數類(lèi)型的轉換。
      為了保證傳遞地址的有效性,還需要使用pjsua_verify_sip_url(url_ptr)驗證,這個(gè)函數主要驗證url_ptr是符合SIP的規則,即是否是一個(gè)合法的SIP地址。然而char*型的地址pjsua中還是不能直接使用的,這是因為pjsua重新封裝了參數類(lèi)型,所以最后還需要將其轉化成pj_ str_t類(lèi)型,pjlib提供pj_str()函數可以完成轉化。在完成了參數的轉化后,調用“pjsua_call_make_call()”,將發(fā)起會(huì )話(huà)。
      (3)hangup函數和pjsua_destroy函數
      這兩個(gè)函數用來(lái)銷(xiāo)毀和掛斷會(huì )話(huà),一般在需結束的時(shí)候調用,它們在接口函數中對應Java_com_android_
      IMSandroid_pjsip_hangup和Java_com_android_IMSandroid_pjsip_destroy,由于沒(méi)有參數傳遞,也沒(méi)有其他的調用,因此這兩個(gè)函數非常簡(jiǎn)單,基本上直接調用pjsua提供的pjsua_call_hangup_all()和pjsua_destroy()就能實(shí)現。pisua中這兩個(gè)函數將完成內存釋放、賬戶(hù)注銷(xiāo)等工作。
      (4)add_account函數
      該函數在基本的pjsua中并不是必須的,但如果要使用SIP服務(wù)器的話(huà),就必須實(shí)現該函數,它在接口函數中對應“Java_com_android_I-MSandroid_pjsip_add_1account”,同“make_call”一樣,也需要傳遞參數,不同的是,它傳遞三個(gè)參數,只是原理大體一樣。
      首先它將參數轉化后保持到cfg,通過(guò)“pjsua_acc_add(&cfg,PJ_TRUE,&ace_id)”將參數添加到pjsua。pjsua將以其中的sip服務(wù)器為目的地址,注冊會(huì )話(huà)發(fā)起申請,經(jīng)過(guò)一系列的操作之后,與目的地址發(fā)起會(huì )話(huà)。
      2.2.4 主程序與用戶(hù)界面
      系統的主程序是一個(gè)標準的Android應用程序,它使用Java語(yǔ)言開(kāi)發(fā),符合SDK規范。與一般的SDK程序不同的是,該類(lèi)中必須使用Syst-em.loadLibrary加載PJSIP庫文件。形式如下:
      System.loadLibrary(“pjsip-jni”);
      其中,pjsip-jni就是上節中PJSIP協(xié)議棧生成的庫。
      主程序中的基本方式均按照上節中的過(guò)程,創(chuàng )建并初始化PJSUA;當call按鍵被觸發(fā)時(shí)發(fā)起會(huì )話(huà),調用make-call()方法;當用戶(hù)接受通話(huà)時(shí),點(diǎn)擊hang或cancel按鍵,觸發(fā)hang()或采用destry()方法等。
      用戶(hù)接口是通過(guò)Android SDK來(lái)實(shí)現的,這部分幾乎全都是Java語(yǔ)言,由于UI不是本文的重點(diǎn),因此只介紹一個(gè)簡(jiǎn)單的界面,實(shí)際應用中用戶(hù)交互是非常重要的。為了實(shí)現所需的功能,至少需要一個(gè)文本框來(lái)提供SIP地址,以及兩個(gè)按鍵來(lái)控制會(huì )話(huà)發(fā)起和結束。另外,在呼叫與通話(huà)過(guò)程中,還需要一個(gè)頁(yè)面來(lái)顯示,這里可以通過(guò)對話(huà)框來(lái)顯示,最后的界面如圖2所示。

      封裝后安裝到Android手機、MID或虛擬機中,并發(fā)起會(huì )話(huà)。與開(kāi)源SIP軟件Linphone通信的結果如圖2所示。
      4 結語(yǔ)
      通過(guò)測試表明,該系統能夠對發(fā)起并很好地控制SIP信令,該系統由于采用SIP協(xié)議,因此與所有采用這一協(xié)議的軟件均能通信,如Lin-phone,Kphone等,功能測試中表現良好,實(shí)現了VoIP的基本需求。同時(shí)如果要增加功能,可以在Java類(lèi)中添加相應的方法并在應用層調用即可,具有一定的可擴展性。
      由于手機等手持設備在規格和配置上的差異,該系統在具體的設備上使用時(shí),界面略有不同,但是同系統架構的手機使用時(shí)并不影響功能,在HTC Desire和MOTO Milestone上測試均能正常使用。但是,當移植到不同的架構時(shí)(即使同時(shí)ARM架構),仍需做一定的優(yōu)化,一般采取主流平臺的多種版本方式來(lái)解決,這也是所有多廠(chǎng)商移動(dòng)設備上一個(gè)無(wú)法避免的問(wèn)題。

    OFweek電子工程網(wǎng)

    相關(guān)閱讀:

    分享到: 收藏

    專(zhuān)題

    亚洲精品网站在线观看不卡无广告,国产a不卡片精品免费观看,欧美亚洲一区二区三区在线,国产一区二区三区日韩 横山县| 龙井市| 永德县| 元谋县| 左权县| 旬阳县| 沂南县| 上犹县| 和静县| 高平市| 南平市| 孟津县| 商河县| 齐河县| 芦山县| 北碚区| 咸丰县| 汕尾市| 东源县| 东平县| 龙泉市| 叶城县| 辛集市| 贵阳市| 定远县| 沽源县| 错那县| 社旗县| 化隆| 丰原市| 汉源县| 和林格尔县| 襄樊市| 治多县| 柏乡县| 梅州市| 和田县| 绩溪县| 安丘市| 九龙县| 银川市| http://444 http://444 http://444 http://444 http://444 http://444