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的更多相关文章

  1. PJSUA2开发文档--第五章 帐户(号)Accounts

    第五章 帐户(号) 帐户提供正在使用该应用程序的用户的身份(或身份).一个帐户有一个与之相关的SIP统一资源标识符(URI).在SIP术语中,该URI用作该人的记录地址( Address of Rec ...

  2. PJSUA2开发文档--第四章 端点ENDPOINT

    4.端点ENDPOINT Endpoint类是一个单例类,应用程序必须在此类实例之前创建一个并且最多只能创建一个,然后才能执行任何操作.同样,一旦这个类被销毁,应用程序就不能调用该库的任何API.这个 ...

  3. PJSUA2开发文档--第六章 媒体 Media类

    6. 媒体(Media) 媒体对象是能够产生媒体或接受媒体的对象. Media的重要子类是AudioMedia,它代表音频媒体.PJSUA2支持多种类型的音频媒体对象: 捕获设备的AudioMedia ...

  4. PJSUA2开发文档--第七章 呼叫 Calls类

    7   呼叫Calls 呼叫由Call类处理 7.1 子类化Call类 要使用Call类,应用程序应创建子类,如: class MyCall : public Call { public: MyCal ...

  5. PJSUA2开发文档--第十一章 网络问题

    11 网络问题 11.1 IP地址更改 请参阅wiki 处理IP地址更改.请注意,本指南使用PJSUA API作为参考. 11.2 被阻止/过滤的网络 请参阅维基百科 通过阻止或过滤的VoIP网络

  6. PJSUA2开发文档--第十二章 PJSUA2 API 参考手册

    12 PJSUA2 API 参考手册 12.1 endpoint.hpp PJSUA2基本代理操作.  namespace pj PJSUA2 API在pj命名空间内. 12.1.1 class En ...

  7. PJSUA2开发文档--第九章 PJSUA2应用程序示例

    9. PJSUA2示例应用程序 9.1 示例应用程序 9.1.1 C++ pjsip-apps/src/samples/pjsua2_demo.cpp 是一个非常简单可用的C++示例应用程序. /* ...

  8. PJSUA2开发文档--第十章 媒体质量(MEDIA QUALITY)

    10 媒体质量(Media Quality) 10.1 音频质量 如果遇到音频质量问题,可尝试以下步骤: 遵循指南:使用pjsystest测试声音设备. 识别声音问题并使用以下步骤进行故障排除:检查声 ...

  9. PJSUA2开发文档--第八章 好友(Buddy)类

    8  好友(存在)Buddy PJSUA2的功能是围绕Buddy类为中心展开的.该类表示一个远端好友(伙伴,一个人或一个SIP端点). 8.1 子类化Buddy类 要使用Buddy类,通常应创建子类, ...

随机推荐

  1. [Swift]LeetCode222. 完全二叉树的节点个数 | Count Complete Tree Nodes

    Given a complete binary tree, count the number of nodes. Note: Definition of a complete binary tree ...

  2. [Swift]LeetCode632. 最小区间 | Smallest Range

    You have k lists of sorted integers in ascending order. Find the smallest range that includes at lea ...

  3. [Swift]LeetCode667. 优美的排列 II | Beautiful Arrangement II

    Given two integers n and k, you need to construct a list which contains n different positive integer ...

  4. winform文件筛选器

    在.net 框架中,微软给我们封装了一个用于打开文件的对话框——OpenFileDialog.而该对话框包含一个可以筛选文件的属性——Filter,利用该属性,可选筛选出我们需要的文件.   因此,我 ...

  5. java中过多if-else分支语句的优化方案

    利用Map优化过的的if-else分支 package com.taiping.test; import java.util.HashMap;import java.util.Map; public ...

  6. github pages代码高亮highlighter

    github pages 一直想添加代码高亮 highlighter ,基于 jekyll 3.0 的 rouge 终于搞定了: 下载代码高亮库 在 cmd 中输入: rougify style mo ...

  7. 【转】ret,retf,iret的区别

    ret RET, and its exact synonym RETN, pop IP or EIP from the stack and transfer control to the new ad ...

  8. Kafka监控系统Kafka Eagle剖析

    1.概述 最近有同学留言反馈了使用Kafka监控工具Kafka Eagle的一些问题,这里笔者特意整理了这些问题.并且希望通过这篇博客来解答这些同学的在使用Kafka Eagle的时候遇到的一些困惑, ...

  9. Java类文件的结构

    Class文件是以8位字节为基础单位的二进制流,各部分中间没有分隔符.遇到8位字节以上的空间数据项时,则会按照高位在前的方式分割成若干个8位字节进行存储. Class文件采用类似C语言的伪结构体来存储 ...

  10. 设计模式总结篇系列:抽象工厂模式(Abstract Factory)

    在上一篇的工厂方法模式中,通过一个公用的类对其他具有相同特性(实现相同接口或继承同一父类)的类的对象进行创建.随之带来的问题在于:当新定义了一个具有相同特性的类时,需要修改工厂类.这与设计模式中的开闭 ...