CSipSimple的第三方编码器是以插件形式集成的,那么它是怎么实现的?我们以音频编码器为例进行说明。

一、何为插件

  工程中有一个包,com.csipsimple.plugins.codecs。从包名来看,应该就是编码器,但是打开发现只有一个文件ReceiverSILK.java,它就是简单的继承了BroadcastReceiver:

public class ReceiverSILK extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
}
}

  为什么onReceive啥都没干,到底是怎么回事?事实上,它是借助了BroadcastReceiver的关于包管理的功能,而不是我们通常用到的广播功能。下面结合代码详细分析。

  既然代码中有这么一个BroadcastReceiver,那么Manifest中应该对其进行注册,果然发现如下代码:

    <receiver
android:name="com.csipsimple.plugins.codecs.ReceiverSILK"
android:exported="false" >
<meta-data
android:name="lib_name"
android:value="libpj_silk_codec.so" />
<meta-data
android:name="init_factory"
android:value="pjmedia_codec_silk_init" /> <intent-filter>
<action android:name="com.csipsimple.codecs.action.REGISTER_CODEC" />
</intent-filter>
</receiver>

  该Receiver接收的action为"com.csipsimple.codecs.action.REGISTER_CODEC"。另外该receiver还附带了两个参数,分别为"lib_name"和"init_factory"。这两个参数说明了解码器的库名称以及初始化函数。这两个参数是实现插件模型的重要组成部分。

二、编码设置

  现在暂时抛开上面的内容,走另外一条路线。sip栈的建立在com.csipsimple.pjsip.PjSipService.java中。

  最关键的是sipStart函数,它进行了一系列的初始化,然后启动sip栈。其中有一段代码是涉及获取音频插件的:

  Map<String, DynCodecInfos> availableCodecs = ExtraPlugins.getDynCodecPlugins(
service, SipManager.ACTION_GET_EXTRA_CODECS);
dynamic_factory[] cssCodecs = cssCfg.getExtra_aud_codecs();
int i = 0;
for (Entry<String, DynCodecInfos> availableCodec : availableCodecs.entrySet()) {
DynCodecInfos dyn = availableCodec.getValue();
if (!TextUtils.isEmpty(dyn.libraryPath)) {
cssCodecs[i].setShared_lib_path(pjsua.pj_str_copy(dyn.libraryPath));
cssCodecs[i++].setInit_factory_name(pjsua
.pj_str_copy(dyn.factoryInitFunction));
}
}
cssCfg.setExtra_aud_codecs_cnt(i);
......

  跳转到getDynCodecPlugins函数:

public static Map<String, DynCodecInfos> getDynCodecPlugins(Context ctxt, String action){
if(!CACHED_RESOLUTION.containsKey(action)) {
HashMap<String, DynCodecInfos> plugins = new HashMap<String, DynCodecInfos>(); PackageManager packageManager = ctxt.getPackageManager();
Intent it = new Intent(action); List<ResolveInfo> availables = packageManager.queryBroadcastReceivers(it, 0);
for(ResolveInfo resInfo : availables) {
ActivityInfo actInfos = resInfo.activityInfo;
if( packageManager.checkPermission(SipManager.PERMISSION_CONFIGURE_SIP, actInfos.packageName) == PackageManager.PERMISSION_GRANTED) {
ComponentName cmp = new ComponentName(actInfos.packageName, actInfos.name);
DynCodecInfos dynInfos;
try {
dynInfos = new DynCodecInfos(ctxt, cmp);
plugins.put(cmp.flattenToString(), dynInfos);
} catch (NameNotFoundException e) {
Log.e(THIS_FILE, "Error while retrieving infos from dyn codec ", e);
}
}
}
CACHED_RESOLUTION.put(action, plugins);
} return CACHED_RESOLUTION.get(action);
}

  仔细分析发现,getDynCodecPlugins函数的功能是借助android的PackageManager获取所有接收相应action的BroadcastReceiver,把相关的信息,即库名和初始化函数名放到DynCodecInfos对象中,并且以Map来组织。

  回到sipStart中,getDynCodecPlugins返回的内容又重新填入cssCodecs,它是cssCfg的一个成员。再往后,cssCfg用于csipsimple的初始化:

status = pjsua.csipsimple_init(cfg, logCfg, mediaCfg, cssCfg, service);

  在sipStart函数的最后,还有两个函数initCodecs()和setCodecsPriorities()涉及编码问题。

  initCodecs:

private void initCodecs() throws SameThreadException {

        synchronized (codecs) {
if (!codecs_initialized) {
int nbrCodecs, i; // Audio codecs
nbrCodecs = pjsua.codecs_get_nbr();
for (i = 0; i < nbrCodecs; i++) {
String codecId = pjStrToString(pjsua.codecs_get_id(i));
codecs.add(codecId);
// Log.d(THIS_FILE, "Added codec " + codecId);
}
// Set it in prefs if not already set correctly
prefsWrapper.setCodecList(codecs); // Video codecs
nbrCodecs = pjsua.codecs_vid_get_nbr();
for (i = 0; i < nbrCodecs; i++) {
String codecId = pjStrToString(pjsua.codecs_vid_get_id(i));
video_codecs.add(codecId);
Log.d(THIS_FILE, "Added video codec " + codecId);
}
// Set it in prefs if not already set correctly
prefsWrapper.setVideoCodecList(video_codecs); codecs_initialized = true;
// We are now always capable of tls and srtp !
prefsWrapper.setLibCapability(PreferencesProviderWrapper.LIB_CAP_TLS, true);
prefsWrapper.setLibCapability(PreferencesProviderWrapper.LIB_CAP_SRTP, true);
}
} }

  该函数是从pjsua中提取出所有的编码名称,其中包括自带的以及以插件形式安装的。以插件形式安装的就是之前由csimple_init注册到pjsua中。initCodecs提取的编码名称是用于更新UI,使得用户可以选择使用哪些编码。

  另一个函数setCodecsPriorities(),其作用就是设置使用的编码。跟进函数可以发现会调用pjsua.codec_set_priority()函数,再往下就是native code了。setCodecsPriorities还作了一些工作就是根据网速类型(fast/slow)对编码进行分类,选择在不同环境下的编码。

三、总结

  将上面的内容综合起来,我们就能够理解CSipSimple的插件模型了。通过BroadcastReceiver注册插件,需要提供一个库名和一个初始化函数名。在编码器初始化的过程中,通过android内部的PackageManager来获得插件的信息,有了这些信息就可以找到编码库,也知道如何调用编码库。我们只需要把这些信息告诉pjsua,其他的东西就不用我们管了。

  当然,这些音频编码库是要符合一定规范的。CSipSimple使用了Opensl ES。只要支持该api的第三方编码库应该都可以继承进来。

  

  

CSipSimple的插件结构的更多相关文章

  1. 【 VS 插件开发 】二、了解Vs插件结构

    [ VS 插件开发 ]二.了解Vs插件结构

  2. Kettle 4.2源码分析第二讲--Kettle插件结构体系简介

    1.  插件体系结构 1.1. 插件技术原理 1.1.1.    插件概念说明 插件是一种遵循统一的预定义接口规范编写出来的程序,应用程序在运行时通过接口规范对插件进行调用,以扩展应用程序的功能.在英 ...

  3. intellij 插件结构(文件结构以及概念层面上的结构)

    1.插件内的文件 2.插件类加载器 3.插件组件(component) 4.插件的扩展以及扩展点(Extensions.Extension Points) 5.插件的Action 6.插件的Servi ...

  4. 自己写jquery插件之模版插件高级篇(一)

    需求场景 最近项目改版中,发现很多地方有这样一个操作(见下图gif动画演示),很多地方都有用到.这里不讨论它的用户体验怎么样. 仅仅是从复用的角度,如果每个页面都去写text和select元素,两个b ...

  5. Chrome插件(Extensions)开发攻略

    本文将从个人经验出发,讲述为什么需要Chrome插件,如何开发,如何调试,到哪里找资料,会遇到怎样的问题以及如何解决等,同时给出一个个人认为的比较典型的例子——获取网页内容,和服务器交互,再把信息反馈 ...

  6. 开源WinForms界面开发框架Management Studio 选项卡文档 插件 Office 2007蓝色风格 后台线程

    Management Studio是我在WinForms小项目开发过程中搭建起来的一个插件式结构的应用程序框架,因为简单灵活又容易扩展,现在将它开源供读者参考. 跑起来的效果图如下所示,具备选项卡式多 ...

  7. 转:用C++实现的一种插件体系结构-----概述

    用C++实现的一种插件体系结构-----概述 本文讨论一种简单却有效的插件体系结构,它使用C++,动态链接库,基于面向对象编程的思想.首先来看一下使用插件机制能给我们带来哪些方面的好处,从而在适当时候 ...

  8. 制作一个简洁的jquery插件

    原文:http://mp.weixin.qq.com/s?__biz=MzAxMzgwNDU3Mg==&mid=401571467&idx=1&sn=08cb00963e6ef ...

  9. [转]nopcommerce商城系统--如何编写一个插件

    本文转自:http://www.cnblogs.com/ganqiyin/p/3680771.html 原址:http://www.nopcommerce.com/docs/77/how-to-wri ...

随机推荐

  1. javascript字符转直接量和转义字符

    直接量: 你可以用单引号或者双引号来表示字符串的直接量.但是js的字符串必须是Unicode 字符序列. 转义字符: 主要用在字符串中,包涵控制字符,以及当前操作系统余元所不允许直接输入的字符. 转义 ...

  2. UVA Open Credit System Uva 11078

    题目大意:给长度N的A1.....An 求(Ai-Aj)MAX 枚举n^2 其实动态维护最大值就好了 #include<iostream> #include<cstdio> u ...

  3. (转)pymysql 连接mysql数据库---不支持中文解决

    往数据库里插入中文时出现异常:UnicodeEncodeError: 'latin-1' codec can't encode characters 就是编码的问题,pymysql默认的编码是lati ...

  4. 转!!Java代码规范、格式化和checkstyle检查配置文档

    为便于规范各位开发人员代码.提高代码质量,研发中心需要启动代码评审机制.为了加快代码评审的速度,减少不必要的时间,可以加入一些代码评审的静态检查工具,另外需要为研发中心配置统一的编码模板和代码格式化模 ...

  5. 非标准JSON解析

    http://blog.csdn.net/superit401/article/details/51734591 String category = "{'v-soft-list':[{ty ...

  6. 【转】 如何使用Valgrind memcheck工具进行C/C++的内存泄漏检测

    系统编程中一个重要的方面就是有效地处理与内存相关的问题.你的工作越接近系统,你就需要面对越多的内存问题.有时这些问题非常琐碎,而更多时候它会演变成一个调试内存问题的恶梦.所以,在实践中会用到很多工具来 ...

  7. 揭开Socket编程的面纱

    对TCP/IP.UDP.Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵.那么我想问: 1. 什么是TCP/IP.UDP? 2. Socket在哪里呢? 3. Sock ...

  8. MySQL使用用户变量更新分组排序

    第一个需求是根据A字段进行排序,排序结果更新到B字段 简单搜索之后,很快得到答案 http://dev.mysql.com/doc/refman/5.7/en/update.html ; ) ORDE ...

  9. mongodb语法备份(转)

    mongodb语法 MongoDB的好处挺多的,比如多列索引,查询时可以用一些统计函数,支持多条件查询,但是目前多表查询是不支持的,可以想办法通过数据冗余来解决多表查询的问题. 查询colls所有数据 ...

  10. MVC5 Entity Framework学习之Entity Framework高级功能(转)

    在之前的文章中,你已经学习了如何实现每个层次结构一个表继承.本节中你将学习使用Entity Framework Code First来开发ASP.NET web应用程序时可以利用的高级功能. 在本节中 ...