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 SDK如ActivePython的-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 = ;
try
  {
ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg);
}
   catch (Error &err)
   {
std::cout << err.info() << std::endl;
return ;
} // 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", , "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(); // Delete the account. This will unregister from server
delete acc; // This will implicitly shutdown the library
return ;
}

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

PJSUA2开发文档--第三章 PJSUA2高级API

 
上一篇:springboot中使用Spring Security 之认证授权注解的使用(三)


下一篇:20145215&20145307信息安全系统设计基础实验报告