如何实现对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程序的入口。几个重要类之间实例化过程,如下图:

如何实现对JVoicebridge的二次开发

图一(实例化过程)

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

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

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

更进一步

Softphone,进行API封装。

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

如何实现对JVoicebridge的二次开发

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

你可能感兴趣的:(bridge)