zoukankan      html  css  js  c++  java
  • CSipSimple的插件结构

      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的第三方编码库应该都可以继承进来。

      

      

  • 相关阅读:
    IntelliJ IDEA Community Edition 14.1.4下使用 Apache-Subversion搭建代码管理环境
    Eclipse+SVN搭建开发环境
    查看分区表中分区详细信息
    asp.net mvc 后台怎么接受前端返回的array list dictionary
    jquery 操作select
    asp.net mvc4 System.Web.Optimization找不到引用
    asp.net mvc4 设置build项目时,编译view页面
    c# ConfigurationSection
    Array基本操作
    For,Function,Lazy
  • 原文地址:https://www.cnblogs.com/zhizhizhiyuan/p/3818902.html
Copyright © 2011-2022 走看看