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;
      }
  }
}

 

 
原文地址:https://www.cnblogs.com/mobilecard/p/6708651.html