如何实现对JVoicebridge的二次开发

如何实现对JVoicebridge的二次开发

作者:kagula

时间:2008-10-23

 

内容简介

JVoicebridge是个开源的音频会议软件。

本文主要介绍把JVoicebridge提供的功能,集成到自己程序中的手段。

JVoicebridge的使用可以参考《JVoiceBridge使用简介》

 

阅读前提

[1]Java开发经验

[2]Eclipse使用经验

[3]svn插件使用经验

[4]如何使用JVoicebridge

 

 

正文

我这里使用的环境是: JDK 1.6.xMyEclipse5.1.x  svn1.2.x

 

进入官网首页,点击项目工具中的subversion项,根据里面的svn地址,把源码download下来。利用源码包中的softphone,新建一个softphone项目。

 

展开softphone源码,com.sun.mc.softphone包中的SipCommunicator类就是Softphone程序的入口。几个重要类之间实例化过程,如下图:

 

 

 

 

图一(实例化过程)

 

  上图红色部份是我们重点要了解的,打开NewPhoneFrame.java文件,这里一个继续JFrameGUI表单类。用户通过它同Softphone的其它部份交互。

NewPhoneFrame的公共控件变量、公共成员函数,都是为了被NewGuiManager调用。而当用户的操作,激发NewPhoneFrame的某部份代码时,它的“执行流”,则会先流到NewGuiManager

如果,你只是想为Softphone换一个人机界面,那就从NewPhoneFrame入手吧!参考NewPhoneFrame为自己写一个MyPhoneFrame,替换NewGuiManager.java中出现的字符串NewPhoneFrameMyPhoneFrame。我这里替换了两次。再次运行SipCommunicator,你会发现Softphone的界面已经换成了你的NewPhoneFrame

 

更进一步

Softphone,进行API封装。

现在我们建立一个新类CSoftphone,用它来封装Softphone的应用程序接口。

 

如何实现对JVoicebridge的二次开发_第1张图片

图二(Softphone类图)

第一步,我们来构造CSoftphone

 

   构造函数代码段如下:

     private SipCommunicator sipCommunicator=null;  //Softphone的全局对象,行28

     private NewGuiManager gm=null;  //GUI管理器,行29

   /**

      * @param args 控制台过来的,参数列表

      * @throws Exception  构造失败,简单抛出异常

      */

     public CSoftphone(String[] args) throws Exception

     {

              try {

            sipCommunicator = new SipCommunicator(args);  //38

              } catch (ParseException e) {

                  SipCommunicator.usage();

                  System.exit(1);

                  throw new Exception("Initialization failed!");

              }

              gm = (NewGuiManager)sipCommunicator.guiManager;   //44

     }

上面的代码片段中,红色部份,是值得注意的。

38代码类同SipCommunicator::main()函数体部份。不要忘记,传给它的参数列表中包含”-nogui”,因为,我们不需要SipCommunicator提供给我们的缺省窗口。

44的代码是为了方便我们以后封装SoftphoneAPI

 

第二步,建立接口

  接口代码如面。这里要注意的是,导入CSoftphone接口,你会发现很多来自Softphone的方法或类,不能被找到,请找到这些源码的位置,把它们的存取属性改为public

  下面是拨号,代码

     /**

      * 拨打电话;它是异步的,你应该从回调函数中,获取,Dial是否成功的结果。请参考setRecall方法。

      * 注意:已经在Dial的状态下,再dial会导致Softphone抛出异常

      * @param strID   PhoneNumber  例如:[email protected] c=cc

其中IP地址,是你bridge所在的地址,c=cc,表示,加入cc会议。

      * @return

      */

     public void dial(String strID)

     {

              //正文     

              gm.alertManager.stopAllAlerts();

              String callee=gm.format(strID);

 

        UserCallInitiationEvent commEvt = new UserCallInitiationEvent(callee);

        for (int i = gm.listeners.size() - 1; i >= 0; i--) {

            ( (UserActionListener) gm.listeners.get(i)).handleDialRequest(commEvt);

        }

     }

    

     /**

      * 实例化这个类的对象后,应该调用下面的成员函数

      * 释放Softphone

      */

     public void exit()

     {

        for (int i = gm.listeners.size() - 1; i >= 0; i--) {

            ( (UserActionListener) gm.listeners.get(i)).handleExitRequest();

        }

     }

注意:exit函数,没有及时调用,可能会造成,你的程序退出了,但是Softphone还没有退出。

接口中封装的代码,都是来自Softphone包的NewGuiManager类,你只要如上,稍微修改,就能直接使用了。

 

第三步,如何回调

    上面的Dial从某种角度上来说是异步的,怎么才能知道,你已经加入了通话状态, 这就需要一个接口。这里我定义了ISoftphone

 

 

图三(ISoftphone接口)

 

   /**

      * 设置状态通知

      * @param objRecall  Softphone会把当前工作情况通知给objRecall对象

      */

     public void setRecall(ISoftphone objRecall)

     {

              gm.listenNGM=objRecall;

     }

假设inst是你的应用对象,那么inst对象,所属的类必须继承ISoftphone。因为,只有这样,Softphone才能将消息,通知给你的应用对象。

  同时,我们还必须稍微修改下NewGuiManager::update函数体,在update函数体的后部份,加入下面的代码:

/*    public ISoftphone listenNGM=null;  这段代码是加在NewGuiManager,成员变量说明中的。

*/

        if(listenNGM!=null)

        {

             listenNGM.updataStatus(state);  // 352

        }

352:的代码,是为了当NewGuiManager::update被激发时,实现ISoftphone接口的listenNGM::updataStatus也能被激发,这样才能把Softphone的状态,通知到你的应用对象。

 

  第四步,如何使用接口

我们的接口,现在全部封装在CSoftphone类中[1]我们先要实例化CSoftphone [2]然后把CSoftphone的实例,告诉你的应用对象。[3]最后在CSoftphone实例中设置你的回调对象。

源码片段如下:

/**

* 开头部份代码,略

*/

Public static void main(String[] args) {

                   //启动Softphone

                   CSoftphone  softphone=null;

/**

*  实例化CSoftphone,并为CSoftphoneargs数组中,添加”-nogui”,屏蔽掉Softphone自己的图形化人机界面。这里的代码略。

*/

                  

                   //启动你的GUI 这里你的应用对象是inst

                   NewJFrame inst = new NewJFrame();

//设置回调,使softphone的状态更新能够通知到inst

                   softphone.setRecall(inst);

//softphone扔给inst,使在应用对象里可以调用它

                   inst.m_softphone = softphone;

/**

*  启动(Launch) inst应用对象,详细的代码略。

*/

后言

    正文对如何实现Softphone的封装做了下简单的介绍,有些实现细节没有谈到,具体请参考CSoftphone.java文件中的内容,它可以给你更进一步的信息。希望此文可以带领你步入Softphone两次开发的大门。

参考资源

[1]JVoicebridge官网  jvoicebridge.dev.java.net

 

附CSoftphone.java源代码

  1. package com.cwebs.softphone;
  2. import java.io.IOException;
  3. import java.text.ParseException;
  4. import com.sun.mc.softphone.SipCommunicator;
  5. import com.sun.mc.softphone.common.Utils;
  6. import com.sun.mc.softphone.gui.InterlocutorUI;
  7. import com.sun.mc.softphone.gui.NewGuiManager;
  8. import com.sun.mc.softphone.gui.event.UserActionListener;
  9. import com.sun.mc.softphone.gui.event.UserCallControlEvent;
  10. import com.sun.mc.softphone.gui.event.UserCallInitiationEvent;
  11. import com.sun.mc.softphone.media.CallDoneListener;
  12. import com.sun.mc.softphone.media.MediaManager;
  13. import com.sun.mc.softphone.media.MediaManagerFactory;
  14. /**
  15.  * 这里定义Softphone的封装接口
  16.  * 警告!这里的接口基本上都未做入口参数,和调用合法性检查
  17.  * 请在你的应用层上完成该工作。
  18.  * 注意:虽然Softphone有Line1~Line4,四个语音通道,但是
  19.  * 根据JVoiceBridge Document,是只用到了Line1,而且
  20.  * 本接口的需求,也没有要求多Line,因此,本接口只使用Line1。
  21.  * @author kagula
  22.  *
  23.  */
  24. public class CSoftphone {
  25.     private SipCommunicator sipCommunicator=null;  //原Softphone的全局对象
  26.     private NewGuiManager gm=null;  //GUI管理器
  27.     
  28.     /** 
  29.      * @param args 控制台过来的,参数列表
  30.      * @throws Exception  构造失败,简单抛出异常
  31.      */
  32.     public CSoftphone(String[] args) throws Exception
  33.     {
  34.         try {
  35.             sipCommunicator = new SipCommunicator(args);
  36.         } catch (ParseException e) {
  37.             SipCommunicator.usage();
  38.             System.exit(1);
  39.             throw new Exception("Initialization failed!");
  40.         }
  41.         gm = (NewGuiManager)sipCommunicator.guiManager;
  42.     }
  43.     
  44.     /**
  45.      * 设置状态通知
  46.      * @param objRecall  Softphone会把当前工作情况通知给objRecall对象
  47.      */
  48.     public void setRecall(ISoftphone objRecall)
  49.     {
  50.         gm.listenNGM=objRecall;
  51.     }
  52.     /**
  53.      * 拨打电话;它是异步的,你应该从回调函数中,获取,Dial是否成功的结果。请参考setRecall方法。
  54.      * 注意:已经在Dial的状态下,再dial会导致Softphone抛出异常
  55.      * @param strID   PhoneNumber
  56.      * @return
  57.      */
  58.     public void dial(String strID)
  59.     {
  60.         //正文    
  61.         gm.alertManager.stopAllAlerts();
  62.         String callee=gm.format(strID);
  63.         UserCallInitiationEvent commEvt = new UserCallInitiationEvent(callee);
  64.         for (int i = gm.listeners.size() - 1; i >= 0; i--) {
  65.             ( (UserActionListener) gm.listeners.get(i)).handleDialRequest(commEvt);
  66.         }
  67.     }
  68.     
  69.     /**
  70.      * 实例化这个类的对象后,应该调用下面的成员函数
  71.      * 释放Softphone
  72.      */
  73.     public void exit()
  74.     {
  75.         for (int i = gm.listeners.size() - 1; i >= 0; i--) {
  76.             ( (UserActionListener) gm.listeners.get(i)).handleExitRequest();
  77.         }
  78.     }
  79.     
  80.     /**
  81.      * 对Softphone进行设置
  82.      * 警告!函数不对strValue的合法性进行检查,
  83.      * 若strValue不在strKey的取值范围,可能会造成Softphone不能正常工作
  84.      * 设置注册表下面位置中的信息
  85.      * HKEY_CURRENT_USER/Software/JavaSoft/Prefs/com/sun/mc/softphone/common
  86.      * @param strKey   取值范围请查看源代码内的注释
  87.      * @param strValue  取值范围请查看源代码内的注释
  88.      */
  89.     public void setPreference(String strKey,String strValue)
  90.     {       
  91.         //media系列设置
  92.         if(strKey.equalsIgnoreCase("CHANNELS"))
  93.         {
  94.             //设置声道,取值范围  1,2
  95.             Utils.setPreference("com.sun.mc.softphone.media.CHANNELS",strValue);
  96.             Utils.setPreference("com.sun.mc.softphone.media.TRANSMIT_CHANNELS",strValue);           
  97.         }
  98.         if(strKey.equalsIgnoreCase("SAMPLE_RATE"))
  99.         {
  100.             //设置采样率,建议8000,取值范围8000,16000
  101.             Utils.setPreference("com.sun.mc.softphone.media.SAMPLE_RATE",strValue);
  102.             Utils.setPreference("com.sun.mc.softphone.media.TRANSMIT_SAMPLE_RATE",strValue);
  103.         }
  104.         if(strKey.equalsIgnoreCase("MICROPHONE_BUFFER_SIZE"))
  105.         {
  106.             //设置Microphone缓存,建议60,取值范围0,大于等于60的正整数
  107.             Utils.setPreference("com.sun.mc.softphone.media.MICROPHONE_BUFFER_SIZE",strValue);
  108.         }
  109.         
  110.         //sip系列设置
  111.         if(strKey.equalsIgnoreCase("REGISTRAR_ADDRESS"))
  112.         {
  113.             //设置bridge服务器地址,例如:192.168.0.112
  114.             Utils.setPreference("com.sun.mc.softphone.sip.REGISTRAR_ADDRESS",strValue);
  115.         }   
  116.         if(strKey.equalsIgnoreCase("USER_NAME"))
  117.         {
  118.             //设置登录到bridge去的,登录名,例如:Administrator
  119.             Utils.setPreference("com.sun.mc.softphone.sip.AUTHENTICATION_USER_NAME",strValue);
  120.             Utils.setPreference("com.sun.mc.softphone.sip.USER_NAME",strValue);
  121.         }       
  122.         if(strKey.equalsIgnoreCase("OUTBOUND_PROXY_ADDRESS"))
  123.         {
  124.             //设置代理服务器地址及端口,缺省值为129.148.75.104:5060//udp
  125.             Utils.setPreference("javax.sip.OUTBOUND_PROXY_ADDRESS",strValue);
  126.         }
  127.         
  128.         //gui系列设置
  129.         if(strKey.equalsIgnoreCase("LAST_FILE_PLAYED"))
  130.         {
  131.             //设置最近播放的文件名,遵循文件名命名规范,中文文件名未做测试。
  132.             Utils.setPreference("com.sun.mc.softphone.gui.LAST_FILE_PLAYED",strValue);
  133.         }
  134.         if(strKey.equalsIgnoreCase("LAST_FILE_RECORDED"))
  135.         {
  136.             //设置最近录音的文件名,遵循文件名命名规范,中文文件名未做测试。
  137.             Utils.setPreference("com.sun.mc.softphone.gui.LAST_FILE_RECORDED",strValue);
  138.         }
  139.     }
  140.     
  141.     /**
  142.      * 从注册表的下面位置中取配置信息
  143.      * HKEY_CURRENT_USER/Software/JavaSoft/Prefs/com/sun/mc/softphone/common
  144.      * @param strKey
  145.      * @return
  146.      */
  147.     public String getPreferece(String strKey)
  148.     {
  149.         //media系列设置
  150.         if(strKey.equalsIgnoreCase("CHANNELS"))
  151.         {
  152.             //设置声道,取值范围  1,2
  153.             return Utils.getPreference("com.sun.mc.softphone.media.CHANNELS");
  154.             //Utils.setPreference("com.sun.mc.softphone.media.TRANSMIT_CHANNELS",strValue);         
  155.         }
  156.         if(strKey.equalsIgnoreCase("SAMPLE_RATE"))
  157.         {
  158.             //设置采样率,建议8000,取值范围8000,16000
  159.             return Utils.getPreference("com.sun.mc.softphone.media.SAMPLE_RATE");
  160.             //Utils.setPreference("com.sun.mc.softphone.media.TRANSMIT_SAMPLE_RATE",strValue);
  161.         }
  162.         if(strKey.equalsIgnoreCase("MICROPHONE_BUFFER_SIZE"))
  163.         {
  164.             //设置Microphone缓存,建议60,取值范围0,大于等于60的正整数
  165.             return Utils.getPreference("com.sun.mc.softphone.media.MICROPHONE_BUFFER_SIZE");
  166.         }
  167.         
  168.         //sip系列设置
  169.         if(strKey.equalsIgnoreCase("REGISTRAR_ADDRESS"))
  170.         {
  171.             //设置bridge服务器地址,例如:192.168.0.112
  172.             return Utils.getPreference("com.sun.mc.softphone.sip.REGISTRAR_ADDRESS");
  173.         }   
  174.         if(strKey.equalsIgnoreCase("USER_NAME"))
  175.         {
  176.             //设置登录到bridge去的,登录名,例如:Administrator
  177.             return Utils.getPreference("com.sun.mc.softphone.sip.AUTHENTICATION_USER_NAME");
  178.             //Utils.setPreference("com.sun.mc.softphone.sip.USER_NAME",strValue);
  179.         }       
  180.         if(strKey.equalsIgnoreCase("OUTBOUND_PROXY_ADDRESS"))
  181.         {
  182.             //设置代理服务器地址及端口,缺省值为129.148.75.104:5060//udp
  183.             return Utils.getPreference("javax.sip.OUTBOUND_PROXY_ADDRESS");
  184.         }
  185.         
  186.         //gui系列设置
  187.         if(strKey.equalsIgnoreCase("LAST_FILE_PLAYED"))
  188.         {
  189.             //设置最近播放的文件名,遵循文件名命名规范,中文文件名未做测试。
  190.             return Utils.getPreference("com.sun.mc.softphone.gui.LAST_FILE_PLAYED");
  191.         }
  192.         if(strKey.equalsIgnoreCase("LAST_FILE_RECORDED"))
  193.         {
  194.             //设置最近录音的文件名,遵循文件名命名规范,中文文件名未做测试。
  195.             return Utils.getPreference("com.sun.mc.softphone.gui.LAST_FILE_RECORDED");
  196.         }
  197.         return null;
  198.     }
  199.     
  200.     /**
  201.      * 开始录音
  202.      * @param strPath  文件名,文件的编码形式为wav,自动存放到当前目录。
  203.      * @param recordingMic 从Mic中记录,建议设为true
  204.      * @param objListener   实现CallDoneListener接口的对象,当Softphone关闭时,该
  205.      * 接口的callDone方法会被调用
  206.      * @throws IOException
  207.      */
  208.     public void startRecording(String strPath,boolean recordingMic,CallDoneListener objListener) throws IOException
  209.     {
  210.         //入口参数检查
  211.         if(strPath==null||strPath.length()<1)
  212.         {
  213.             return;
  214.         }
  215.         
  216.         //正文
  217.         MediaManager mediaManager;
  218.         mediaManager=MediaManagerFactory.getInstance();
  219.         if(mediaManager!=null)
  220.         {
  221.             mediaManager.startRecording(strPath, new String("au"), recordingMic, objListener);          
  222.         }
  223.     }
  224.     
  225.     /**
  226.      * 录音结束
  227.      * @param recordingMic 从Mic中记录,建议设为true
  228.      */
  229.     public void stopRecording(boolean recordingMic)
  230.     {
  231.         //正文
  232.         MediaManager mediaManager;
  233.         mediaManager=MediaManagerFactory.getInstance();
  234.         if(mediaManager!=null)
  235.         {
  236.             mediaManager.stopRecording(recordingMic);           
  237.         }
  238.     }
  239.     
  240.     /**
  241.      * 挂断Dialing
  242.      *
  243.      */
  244.     public void hangup()
  245.     {
  246.         InterlocutorUI inter = gm.interlocutors.getInterlocutorAt(0);
  247.         if(inter!=null) {
  248.             UserCallControlEvent commEvt = new UserCallControlEvent(inter);
  249.             for (int i = gm.listeners.size() - 1; i >= 0; i--) {
  250.                 ( (UserActionListener) gm.listeners.get(i)).handleHangupRequest(
  251.                     commEvt);
  252.             }
  253.         }
  254.     }
  255.     
  256.     /**
  257.      * 应答
  258.      *
  259.      */
  260.     public void answer()
  261.     {
  262.         InterlocutorUI inter = gm.interlocutors.getInterlocutorAt(0);
  263.         if(inter!=null) {
  264.             UserCallControlEvent commEvt = new UserCallControlEvent(inter);
  265.             for (int i = gm.listeners.size() - 1; i >= 0; i--) {
  266.                 ( (UserActionListener) gm.listeners.get(i)).handleAnswerRequest(
  267.                     commEvt);
  268.             }
  269.         }       
  270.     }
  271.     
  272.     /**
  273.      * 静音
  274.      *
  275.      */
  276.     public void mute()
  277.     {
  278.         InterlocutorUI inter = gm.interlocutors.getInterlocutorAt(0);
  279.         if(inter!=null) {
  280.             UserCallControlEvent commEvt = new UserCallControlEvent(inter);
  281.             for (int i = gm.listeners.size() - 1; i >= 0; i--) {
  282.                 ( (UserActionListener) gm.listeners.get(i)).handleMuteRequest(
  283.                     commEvt);
  284.             }
  285.         }
  286.     }
  287. }

 

你可能感兴趣的:(exception,String,File,null,buffer,subversion)