4 PJSUA操作
在上面的添加使用者操作中,其實已經和底層的JNI打交道了,如執行SipService.setAccountRegistration()這個函數之後就會調用底層的JNI庫。而在執行這些JNI庫之前。又關于PJSUA的一些初始化設定。這一節主要說明這個。
在說明進行初始化之前,先說明下PJSIP庫架構,其架構如圖12所示:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0NXYFhGd192UvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2Lc1TPRRmNONDW3BnblZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DN4QTOzgDNwETNxkDMzEDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
圖12 PJSIP架構
圖12展示了PJSIP架構的各子產品,可以看出從上到下,Application(pjsua)子產品可調用下層所有的子產品,也即是PJSUA處于最高層,其整合了下層子產品的全部功能,是以這也就是為什麼我們基本的操作都在PJSUA這裡進行。是因為通過PJSUA,我們就能很友善的深入到其他子產品中。接着Application子產品往下就是PJSUA_lib層,要讓應用層(PJSUA)能更好的調用,當然得有個封裝好的庫,這個庫就是PJSUA_LIB庫,稱為高層使用者代理庫,集合SIP,Media以及NAT穿越,是以也就有了往下的PJMEDIA-CODEC和PJMEDIA(負責SDP協商媒體編碼和媒體傳輸),PJNATH(解決NAT穿越),PJSUA-UA(提供SIP使用者代理庫),PJSIP-SIMPLE(實作presence和及時消息),PJSIP(核協定棧,SIP協定),PJLIB-UTIL(提供有用的工具函數)以及PJLIB(每個功能根據其所在的層次以及負責的功能提供豐富的接口)等子產品。
知道了PJSIP的基本架構之後,我們回到CSipSimple,在SIP_HOME UI中的OnResume函數下會進行SipService開啟。即通過如圖13語句之後進入SipService。
// Service monitoring stuff
private void startSipService() {
Thread t = new Thread("StartSip") {
public void run() {
Intent serviceIntent = new Intent(SipManager.INTENT_SIP_SERVICE);
serviceIntent.putExtra(SipManager.EXTRA_OUTGOING_ACTIVITY, new ComponentName(SipHome.this, SipHome.class));
startService(serviceIntent); //啟動了SipService
postStartSipService();
};
};
t.start();
}
圖13 通過Intent啟動SipService
Service的生命周期因不同的啟動方式會有所不同,這裡是通過context.startService啟動的。是以其生命周期如下:context.startService() ->onCreate()- >onStart()->Service running-->context.stopService()->onDestroy() ->Service stop。通過onStart()函數,加載JNI庫(會跳到PjSipService的tryToLoadStack方法執行),如圖14所示:
public boolean tryToLoadStack() {
if (hasSipStack) {
return true;
}
// File stackFile = NativeLibManager.getStackLibFile(service);
if (!sipStackIsCorrupted) {
try {
// Try to load the stack
// System.load(NativeLibManager.getBundledStackLibFile(service,
// "libcrypto.so").getAbsolutePath());
// System.load(NativeLibManager.getBundledStackLibFile(service,
// "libssl.so").getAbsolutePath());
// System.loadLibrary("crypto");
// System.loadLibrary("ssl");
System.loadLibrary(NativeLibManager.STD_LIB_NAME); //加載jni本地庫
System.loadLibrary(NativeLibManager.STACK_NAME);
hasSipStack = true;
return true;
} catch (UnsatisfiedLinkError e) {
...
} catch (Exception e) {
...
}
}
return false;
}
圖14 PjSipService類加載JNI庫
而何時進行PJSUA初始化,CSipSimple中,在SipService開始,通過相關的調用完成的。從圖7可看到,當注冊完廣播接收器之後還進行了這一步:deviceStateReceiver.startMonitoring()。這是程式進入DynamicReciever4的startMonitoring()函數,進而在onConnectivityChanged()函數中調用了SipService的restartSipStack函數,進而進入startSipStack()函數并調用了pjService.sipStart()方法。而這個sipStart()函數就是PJSUA初始化的所在處。
我們進入sipStart函數,結合PJSIP開發文檔。從開發文檔中我們知道,基礎的PJSUA-API控制PJSUA的建立,初始化,啟動,同時還提供各種輔助功能。在sipStart函數中,通過status = pjsua.create();完成pjsua的建立,除其他事項外,還初始化PJLIB,PJLIB-UTIL,并建立了一個SIP endpoint。在調用任何PJLIB功能之前這是至關重要的一步。
進行了上面的初始化之後,并進行一些General Configure。一般情況下,應用程式都将通常需要執行一些任務,使用pjsua_transport_create()方法(該方法是C語言)來建立SIP傳輸。故在CSipSimple中有createTransport這樣的方法來建立SIP傳輸。完成這些初始化之後,應用程式必須調用status = pjsua.start()開始PJSUA,此函數将檢查所有的配置是否正确,如果沒有則采用預設配置。
經過上面的初始化之後,我們就可以進一步解釋2.3節的添加使用者操作中,PJSUA是如何操作的。
在2.3節中已經說過,使用者添加是從接收器DynamicReceiver4執行了SipService.setAccountRegistration這個函數開始的,确切的講,連接配接底層JNI庫的都在PjSipService中進行,此處也是這樣。進入到PjSipService中的setAccountRegistration方法。然後根據account的不同情況進行不同的操作,首次添加使用者将會進行這一步status = pjsua.acc_add(account.cfg, pjsuaConstants.PJ_FALSE, accId)。這裡acc_add即調用了底層的JNI庫的pjsuaJNI.acc_add,進而傳回添加成功與否的狀态。根據該狀态進行相應狀态的改變,進而改變顯示界面,這也是2.3節中最後一點講到的内容觀察者ContentObserver的作用。