zoukankan      html  css  js  c++  java
  • 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;
          }
      }
    }

     

     
  • 相关阅读:
    EF数据迁移完整步骤
    ajax跨域最全解决方案
    WPF控件与WPF窗体
    WPF模板是把控件MVC模式化
    对象与类型
    Java加权负载均衡策略
    db2列式存储
    linux离线安装mongodb及java调用
    python合并目录下excel数据
    python多线程迁移db2数仓9T数据
  • 原文地址:https://www.cnblogs.com/mobilecard/p/6708651.html
Copyright © 2011-2022 走看看