PJSUA2開發文檔--第三章 PJSUA2高級API


 

3. PJSUA2高級API 

PJSUA2是PJSUA API以上的面向對象抽象。它為構建會話發起協議(SIP)多媒體用戶代理應用程序(也稱為IP / VoIP軟電話)提供高級API。它將信令,媒體和NAT穿越功能結合到易於使用的呼叫控制API,帳戶管理,好友列表管理,在線狀態和即時消息中,以及多媒體功能,如本地會議,文件流,本地播放和語音錄制和強大的NAT穿越技術,利用STUN,TURN和ICE。

PJSUA2在PJSUA-LIB API之上實現。SIP和媒體功能和對象建模遵循PJSUA-LIB提供的(例如,我們還有帳戶,通話,好友等),但訪問它們的API是不同的。這些功能將在本章后面介紹。PJSUA2是一個C ++庫,你可以找到在pjsip目錄中的PJSIP分布。C ++庫可以直接由本地C ++應用程序使用。但PJSUA2不僅僅是一個C ++庫。從一開始,它被設計為可以從高級非本地語言(如Java和Python)訪問。這是通過SWIG綁定來實現的。感謝SWIG,將來可以相對容易地添加與其他語言的綁定。

PJSUA2 API聲明可以pjsip/include/pjsua2在源代碼所在的位置找到pjsip/src/pjsua2當您編譯PJSIP時,它將自動構建。

3.1 PJSUA2主類

以下是PJSUA2的主要類別:

3.1.1終端 Endpoint

這是PJSUA2的主要類別。您需要實例化這個類中的一個,並且從實例中可以初始化並啟動庫。

3.1.2 帳號 Account

帳戶指定SIP會話一側的人員(或端點)的身份。在其他任何事情之前,至少需要創建一個帳戶實例,並且可以從帳戶實例開始創建/接收電話以及添加好友。

3.1.3 媒體 Media

這是一個抽象基類,表示能夠生成媒體或傳播媒體的媒體元素。將 AudioMedia 子類化,然后將其子類實例化成具體類,如 AudioMediaPlayer 和 AudioMediaRecorder

3.1.4 呼叫 Call

該類表示正在進行的呼叫(或者說技術上是INVITE會話),並且可以用於操縱它,例如應答呼叫,掛斷呼叫,保持呼叫,轉接呼叫等。

3.1.5 搭檔 Buddy

該類代表一個遠程伙伴(一個人或一個SIP端點)。您可以訂閱好友的狀態來了解好友是否在線/離線等等,您可以向/從伙伴發送和接收即時消息。

 

 

3.2 一般概念

3.2.1 類使用模式

使用上面的主類的方法,可以很容易地調用對象的各種操作。但是我們如何從這些類中獲取事件/通知?以上每個主要類(Media除外)將在回調方法中獲取他們的事件。所以要處理這些事件,只需從對應的類(Endpoint,Call,Account或Buddy)派生一個類,並實現/重載相關的方法(取決於想要處理的事件)。更多內容將在后面的章節中進行說明。

3.2.2錯誤處理

使用異常作為報告錯誤的手段,因為這將使程序更自然地流動。產生錯誤的操作會引起錯誤異常。如果希望以更結構化的方式顯示錯誤,則Error類有幾個成員來解釋錯誤,例如引發錯誤的操作名稱,錯誤代碼和錯誤消息本身。

3.2.3 異步操作

如果您已經使用PJSIP開發應用程序,那么您已經了解了這些應用程序。在PJSIP中,涉及發送和接收SIP消息的所有操作都是異步的,這意味着調用該操作的功能將立即完成,您將在回調中獲得完成狀態。

例如Call類的makeCall( ) 方法。此功能用於啟動到目的地的呼出。當此函數成功返回時,並不意味着該呼叫已經建立,而是意味着該呼叫已成功啟動。您將在Call類的onCallState()回調方法中獲取呼叫進度和/或完成的報告。

3.2.4 線程

對於需要輪詢的平台,PJSUA2模塊提供自己的工作線程來輪詢PJSIP,因此無需實例化您的輪詢線程。如前所述,應用程序應該准備好讓主線程調用不同線程的回調。PJSUA2模塊本身是線程安全的。

通常,尤其是如果使用高級語言(如Python)調用PJSUA2,則需要通過將EpConfig.uaConfig.threadCnt 設置為0,來禁用PJSUA2內部工作線程。因為高級環境不喜歡被外部線程調用(如PJSIP的工作線程)。

 

3.2.5 垃圾收集問題

垃圾收集(Garbage collection,GC)存在於Java和Python(和其他語言,但現在我們不支持這些),並且在PJSUA2使用方面存在一些問題:

  1. 在Java和Python空間中創建的PJSUA2對象的過早析構,並傳遞給本機空間,而不保留對對象的引用
  2. 它延遲了對象(包括PJSUA2對象)的析構,導致對象的析構函數中的代碼無序執行
  3. GC的銷毀操作可以在之前未注冊到PJLIB的不同線程上運行,從而導致斷言assertion

當使用 Account.addBuddy()或者通過調用 EpConfig.LogConfig.setLogWriter()設置LogWriter,將Buddy對象添加到一個帳戶時,問題1的一些示例(這些示例絕不是完整的列表)。為了避免這個問題,應用程序需要維護在其應用程序中創建的對象的顯式引用,而不是依賴於PJSUA2本機庫來跟蹤這些對象,如:

class MyApp {
    private MyLogWriter logWriter;

    public void init()
    {
        /* Maintain reference to log writer to avoid premature cleanup by GC */
        logWriter = new MyLogWriter();
        epConfig.getLogConfig.setWriter(logWriter);
    }
}

對於問題2和3,應用程序必須立即(使用對象的delete()方法(在Java中))來銷毀PJSUA2對象,而不是依靠GC來清理對象。例如,刪除一個帳戶,是不能夠讓它離開控制范圍的。應用程序必須手動刪除它(在Java中):

acc.delete();

  

3.2.6 對象持久化

PJSUA2包括 PersistentObject(持久對象) 類,用於提供從文檔(字符串或文件)讀取/寫入數據的功能。數據可以是簡單的數據類型,如布爾值,數字,字符串和字符串數組,或用戶定義的對象。目前的實現了支持從JSON文件讀取和寫入到JSON文件([ http://tools.ietf.org/html/rfc4627 RFC 4627]),但該框架允許應用來擴展API以支持其他的文檔格式。

因此,從PersistentObject繼承的類,如EpConfig(端點配置),AccountConfig(帳戶配置)和BuddyConfig(好友配置),可以從文件加載/保存到文件。

舉個例子來保存配置文件:

 

EpConfig epCfg;
JsonDocument jDoc;
epCfg.uaConfig.maxCalls = 61;
epCfg.uaConfig.userAgent = "Just JSON Test";
jDoc.writeObject(epCfg);
jDoc.saveFile("jsontest.js");

從文件加載:

EpConfig epCfg;
JsonDocument jDoc;
jDoc.loadFile("jsontest.js");
jDoc.readObject(epCfg);

 

3.3 構建(Building) PJSUA2 

PJSUA2 C ++庫將由PJSIP構建系統默認構建。需要標准C++庫。

3.4 構建Python和Java SWIG模塊

對於Python和Java的SWIG模塊,是通過在目錄“pjsip-apps/src/swig” 下調用內置 make和手動make install。make install將安裝Python SWIG模塊到用戶的 site-packages 目錄

3.4.1要求

  1. SWIG
  2. JDK
  3. Python,建議使用2.7或更高版本(我們的Python示例應用程序pygui需要2.7或更高版本,但是pjsua2 Python綁定應該能夠在舊版本上運行)。對於Linux / UNIX,還需要Python developent package(python-devel(如在Fedora上)或python2.7-dev(如在Ubuntu上))。對於Windows,需要MinGW和Python SDKActivePython的-2.7.5(來自ActiveState)。

 

3.4.2測試安裝

要測試安裝,只需運行python和import pjsua2 module:

$ python
> import pjsua2
> ^Z

3.5 在C++應用程序中使用

正如在前面的章節中提到的,一個C++應用程序可以使用pjsua2本身,而在同一時間仍然有權訪問低層對象和擴展庫的能力(如果需要)。使用API​​將與本書中編寫的API參考完全相同。

這是一個完整的C++應用程序示例,可以讓您了解API。下面的代碼段,初始化庫,並創建一個注冊到我們pjsip.org 的SIP服務器的帳戶。

#include <pjsua2.hpp>
#include <iostream>

using namespace pj;

// Subclass to extend the Account and get notifications etc.
class MyAccount : public Account 
{
public: virtual void onRegState(OnRegStateParam &prm)
  { AccountInfo ai
= getInfo(); std::cout << (ai.regIsActive? "*** Register:" : "*** Unregister:") << " code=" << prm.code << std::endl; } }; int main() { Endpoint ep; ep.libCreate(); // Initialize endpoint EpConfig ep_cfg; ep.libInit( ep_cfg ); // Create SIP transport. Error handling sample is shown TransportConfig tcfg; tcfg.port = 5060; try
  { ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg); }
  
catch (Error &err)
   { std::cout
<< err.info() << std::endl; return 1; } // Start the library (worker threads etc) ep.libStart(); std::cout << "*** PJSUA2 STARTED ***" << std::endl; // Configure an AccountConfig AccountConfig acfg; acfg.idUri = "sip:test@pjsip.org"; acfg.regConfig.registrarUri = "sip:pjsip.org"; AuthCredInfo cred("digest", "*", "test", 0, "secret"); acfg.sipConfig.authCreds.push_back( cred ); // Create the account MyAccount *acc = new MyAccount; acc->create(acfg); // Here we don't have anything else to do.. pj_thread_sleep(10000); // Delete the account. This will unregister from server delete acc; // This will implicitly shutdown the library return 0; }

 

3.6 在Python應用程序中使用

中上面的C ++示例代碼等價如下Python代碼:

# Subclass to extend the Account and get notifications etc.
class Account(pj.Account):
  def onRegState(self, prm):
      print "***OnRegState: " + prm.reason

# pjsua2 test function
def pjsua2_test():
  # Create and initialize the library
  ep_cfg = pj.EpConfig()
  ep = pj.Endpoint()
  ep.libCreate()
  ep.libInit(ep_cfg)

  # Create SIP transport. Error handling sample is shown
  sipTpConfig = pj.TransportConfig();
  sipTpConfig.port = 5060;
  ep.transportCreate(pj.PJSIP_TRANSPORT_UDP, sipTpConfig);
  # Start the library
  ep.libStart();

  acfg = pj.AccountConfig();
  acfg.idUri = "sip:test@pjsip.org";
  acfg.regConfig.registrarUri = "sip:pjsip.org";
  cred = pj.AuthCredInfo("digest", "*", "test", 0, "pwtest");
  acfg.sipConfig.authCreds.append( cred );
  # Create the account
  acc = Account();
  acc.create(acfg);
  # Here we don't have anything else to do..
  time.sleep(10);

  # Destroy the library
  ep.libDestroy()

#
# main()
#
if __name__ == "__main__":
  pjsua2_test()

3.7 在Java應用程序中使用

上面的C ++示例代碼等價如下Java代碼:

import org.pjsip.pjsua2.*;

// Subclass to extend the Account and get notifications etc.
class MyAccount extends Account {
  @Override
  public void onRegState(OnRegStateParam prm) {
      System.out.println("*** On registration state: " + prm.getCode() + prm.getReason());
  }
}

public class test {
  static {
      System.loadLibrary("pjsua2");
      System.out.println("Library loaded");
  }

  public static void main(String argv[]) {
      try {
          // Create endpoint
          Endpoint ep = new Endpoint();
          ep.libCreate();
          // Initialize endpoint
          EpConfig epConfig = new EpConfig();
          ep.libInit( epConfig );
          // Create SIP transport. Error handling sample is shown
          TransportConfig sipTpConfig = new TransportConfig();
          sipTpConfig.setPort(5060);
          ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_UDP, sipTpConfig);
          // Start the library
          ep.libStart();

          AccountConfig acfg = new AccountConfig();
          acfg.setIdUri("sip:test@pjsip.org");
          acfg.getRegConfig().setRegistrarUri("sip:pjsip.org");
          AuthCredInfo cred = new AuthCredInfo("digest", "*", "test", 0, "secret");
          acfg.getSipConfig().getAuthCreds().add( cred );
          // Create the account
          MyAccount acc = new MyAccount();
          acc.create(acfg);
          // Here we don't have anything else to do..
          Thread.sleep(10000);
          /* Explicitly delete the account.
           * This is to avoid GC to delete the endpoint first before deleting
           * the account.
           */
          acc.delete();

          // Explicitly destroy and delete endpoint
          ep.libDestroy();
          ep.delete();

      } catch (Exception e) {
          System.out.println(e);
          return;
      }
  }
}

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM