天天看點

CSipSimple程式之基本功能(三)

4 PJSUA操作

在上面的添加使用者操作中,其實已經和底層的JNI打交道了,如執行SipService.setAccountRegistration()這個函數之後就會調用底層的JNI庫。而在執行這些JNI庫之前。又關于PJSUA的一些初始化設定。這一節主要說明這個。

在說明進行初始化之前,先說明下PJSIP庫架構,其架構如圖12所示:

CSipSimple程式之基本功能(三)

圖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的作用。