如何實作對JVoicebridge的二次開發
作者:kagula
時間:2008-10-23
内容簡介
JVoicebridge是個開源的音頻會議軟體。
本文主要介紹把JVoicebridge提供的功能,內建到自己程式中的手段。
JVoicebridge的使用可以參考《JVoiceBridge使用簡介》
閱讀前提
[1]Java開發經驗
[2]Eclipse使用經驗
[3]svn插件使用經驗
[4]如何使用JVoicebridge
正文
我這裡使用的環境是: JDK 1.6.x,MyEclipse5.1.x svn1.2.x
進入官網首頁,點選項目工具中的subversion項,根據裡面的svn位址,把源碼download下來。利用源碼包中的softphone,建立一個softphone項目。
展開softphone源碼,com.sun.mc.softphone包中的SipCommunicator類就是Softphone程式的入口。幾個重要類之間執行個體化過程,如下圖:

圖一(執行個體化過程)
上圖紅色部份是我們重點要了解的,打開NewPhoneFrame.java檔案,這裡一個繼續JFrame的GUI表單類。使用者通過它同Softphone的其它部份互動。
NewPhoneFrame的公共控件變量、公共成員函數,都是為了被NewGuiManager調用。而當使用者的操作,激發NewPhoneFrame的某部份代碼時,它的“執行流”,則會先流到NewGuiManager。
如果,你隻是想為Softphone換一個人機界面,那就從NewPhoneFrame入手吧!參考NewPhoneFrame為自己寫一個MyPhoneFrame,替換NewGuiManager.java中出現的字元串NewPhoneFrame為MyPhoneFrame。我這裡替換了兩次。再次運作SipCommunicator,你會發現Softphone的界面已經換成了你的NewPhoneFrame。
更進一步
對Softphone,進行API封裝。
現在我們建立一個新類CSoftphone,用它來封裝Softphone的應用程式接口。
圖二(Softphone類圖)
第一步,我們來構造CSoftphone
構造函數代碼段如下:
private SipCommunicator sipCommunicator=null; //原Softphone的全局對象,行28
private NewGuiManager gm=null; //GUI管理器,行29
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:的代碼是為了友善我們以後封裝Softphone的API。
第二步,建立接口
接口代碼如面。這裡要注意的是,導入CSoftphone接口,你會發現很多來自Softphone的方法或類,不能被找到,請找到這些源碼的位置,把它們的存取屬性改為public 。
下面是撥号,代碼
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);
}
}
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接口)
public void setRecall(ISoftphone objRecall)
{
gm.listenNGM=objRecall;
}
假設inst是你的應用對象,那麼inst對象,所屬的類必須繼承ISoftphone。因為,隻有這樣,Softphone才能将消息,通知給你的應用對象。
同時,我們還必須稍微修改下NewGuiManager::update函數體,在update函數體的後部份,加入下面的代碼:
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;
//啟動你的GUI 這裡你的應用對象是inst
NewJFrame inst = new NewJFrame();
//設定回調,使softphone的狀态更新能夠通知到inst
softphone.setRecall(inst);
//把softphone扔給inst,使在應用對象裡可以調用它
inst.m_softphone = softphone;
後言
正文對如何實作Softphone的封裝做了下簡單的介紹,有些實作細節沒有談到,具體請參考CSoftphone.java檔案中的内容,它可以給你更進一步的資訊。希望此文可以帶領你步入Softphone兩次開發的大門。
參考資源
[1]JVoicebridge官網 jvoicebridge.dev.java.net
附CSoftphone.java源代碼
- package com.cwebs.softphone;
- import java.io.IOException;
- import java.text.ParseException;
- import com.sun.mc.softphone.SipCommunicator;
- import com.sun.mc.softphone.common.Utils;
- import com.sun.mc.softphone.gui.InterlocutorUI;
- import com.sun.mc.softphone.gui.NewGuiManager;
- import com.sun.mc.softphone.gui.event.UserActionListener;
- import com.sun.mc.softphone.gui.event.UserCallControlEvent;
- import com.sun.mc.softphone.gui.event.UserCallInitiationEvent;
- import com.sun.mc.softphone.media.CallDoneListener;
- import com.sun.mc.softphone.media.MediaManager;
- import com.sun.mc.softphone.media.MediaManagerFactory;
- public class CSoftphone {
- private SipCommunicator sipCommunicator=null; //原Softphone的全局對象
- private NewGuiManager gm=null; //GUI管理器
- public CSoftphone(String[] args) throws Exception
- {
- try {
- sipCommunicator = new SipCommunicator(args);
- } catch (ParseException e) {
- SipCommunicator.usage();
- System.exit(1);
- throw new Exception("Initialization failed!");
- }
- gm = (NewGuiManager)sipCommunicator.guiManager;
- }
- public void setRecall(ISoftphone objRecall)
- {
- gm.listenNGM=objRecall;
- }
- 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);
- }
- }
- public void exit()
- {
- for (int i = gm.listeners.size() - 1; i >= 0; i--) {
- ( (UserActionListener) gm.listeners.get(i)).handleExitRequest();
- }
- }
- public void setPreference(String strKey,String strValue)
- {
- //media系列設定
- if(strKey.equalsIgnoreCase("CHANNELS"))
- {
- //設定聲道,取值範圍 1,2
- Utils.setPreference("com.sun.mc.softphone.media.CHANNELS",strValue);
- Utils.setPreference("com.sun.mc.softphone.media.TRANSMIT_CHANNELS",strValue);
- }
- if(strKey.equalsIgnoreCase("SAMPLE_RATE"))
- {
- //設定采樣率,建議8000,取值範圍8000,16000
- Utils.setPreference("com.sun.mc.softphone.media.SAMPLE_RATE",strValue);
- Utils.setPreference("com.sun.mc.softphone.media.TRANSMIT_SAMPLE_RATE",strValue);
- }
- if(strKey.equalsIgnoreCase("MICROPHONE_BUFFER_SIZE"))
- {
- //設定Microphone緩存,建議60,取值範圍0,大于等于60的正整數
- Utils.setPreference("com.sun.mc.softphone.media.MICROPHONE_BUFFER_SIZE",strValue);
- }
- //sip系列設定
- if(strKey.equalsIgnoreCase("REGISTRAR_ADDRESS"))
- {
- //設定bridge伺服器位址,例如:192.168.0.112
- Utils.setPreference("com.sun.mc.softphone.sip.REGISTRAR_ADDRESS",strValue);
- }
- if(strKey.equalsIgnoreCase("USER_NAME"))
- {
- //設定登入到bridge去的,登入名,例如:Administrator
- Utils.setPreference("com.sun.mc.softphone.sip.AUTHENTICATION_USER_NAME",strValue);
- Utils.setPreference("com.sun.mc.softphone.sip.USER_NAME",strValue);
- }
- if(strKey.equalsIgnoreCase("OUTBOUND_PROXY_ADDRESS"))
- {
- //設定代理伺服器位址及端口,預設值為129.148.75.104:5060//udp
- Utils.setPreference("javax.sip.OUTBOUND_PROXY_ADDRESS",strValue);
- }
- //gui系列設定
- if(strKey.equalsIgnoreCase("LAST_FILE_PLAYED"))
- {
- //設定最近播放的檔案名,遵循檔案名命名規範,中文檔案名未做測試。
- Utils.setPreference("com.sun.mc.softphone.gui.LAST_FILE_PLAYED",strValue);
- }
- if(strKey.equalsIgnoreCase("LAST_FILE_RECORDED"))
- {
- //設定最近錄音的檔案名,遵循檔案名命名規範,中文檔案名未做測試。
- Utils.setPreference("com.sun.mc.softphone.gui.LAST_FILE_RECORDED",strValue);
- }
- }
- public String getPreferece(String strKey)
- {
- //media系列設定
- if(strKey.equalsIgnoreCase("CHANNELS"))
- {
- //設定聲道,取值範圍 1,2
- return Utils.getPreference("com.sun.mc.softphone.media.CHANNELS");
- //Utils.setPreference("com.sun.mc.softphone.media.TRANSMIT_CHANNELS",strValue);
- }
- if(strKey.equalsIgnoreCase("SAMPLE_RATE"))
- {
- //設定采樣率,建議8000,取值範圍8000,16000
- return Utils.getPreference("com.sun.mc.softphone.media.SAMPLE_RATE");
- //Utils.setPreference("com.sun.mc.softphone.media.TRANSMIT_SAMPLE_RATE",strValue);
- }
- if(strKey.equalsIgnoreCase("MICROPHONE_BUFFER_SIZE"))
- {
- //設定Microphone緩存,建議60,取值範圍0,大于等于60的正整數
- return Utils.getPreference("com.sun.mc.softphone.media.MICROPHONE_BUFFER_SIZE");
- }
- //sip系列設定
- if(strKey.equalsIgnoreCase("REGISTRAR_ADDRESS"))
- {
- //設定bridge伺服器位址,例如:192.168.0.112
- return Utils.getPreference("com.sun.mc.softphone.sip.REGISTRAR_ADDRESS");
- }
- if(strKey.equalsIgnoreCase("USER_NAME"))
- {
- //設定登入到bridge去的,登入名,例如:Administrator
- return Utils.getPreference("com.sun.mc.softphone.sip.AUTHENTICATION_USER_NAME");
- //Utils.setPreference("com.sun.mc.softphone.sip.USER_NAME",strValue);
- }
- if(strKey.equalsIgnoreCase("OUTBOUND_PROXY_ADDRESS"))
- {
- //設定代理伺服器位址及端口,預設值為129.148.75.104:5060//udp
- return Utils.getPreference("javax.sip.OUTBOUND_PROXY_ADDRESS");
- }
- //gui系列設定
- if(strKey.equalsIgnoreCase("LAST_FILE_PLAYED"))
- {
- //設定最近播放的檔案名,遵循檔案名命名規範,中文檔案名未做測試。
- return Utils.getPreference("com.sun.mc.softphone.gui.LAST_FILE_PLAYED");
- }
- if(strKey.equalsIgnoreCase("LAST_FILE_RECORDED"))
- {
- //設定最近錄音的檔案名,遵循檔案名命名規範,中文檔案名未做測試。
- return Utils.getPreference("com.sun.mc.softphone.gui.LAST_FILE_RECORDED");
- }
- return null;
- }
- public void startRecording(String strPath,boolean recordingMic,CallDoneListener objListener) throws IOException
- {
- //入口參數檢查
- if(strPath==null||strPath.length()<1)
- {
- return;
- }
- //正文
- MediaManager mediaManager;
- mediaManager=MediaManagerFactory.getInstance();
- if(mediaManager!=null)
- {
- mediaManager.startRecording(strPath, new String("au"), recordingMic, objListener);
- }
- }
- public void stopRecording(boolean recordingMic)
- {
- //正文
- MediaManager mediaManager;
- mediaManager=MediaManagerFactory.getInstance();
- if(mediaManager!=null)
- {
- mediaManager.stopRecording(recordingMic);
- }
- }
- public void hangup()
- {
- InterlocutorUI inter = gm.interlocutors.getInterlocutorAt(0);
- if(inter!=null) {
- UserCallControlEvent commEvt = new UserCallControlEvent(inter);
- for (int i = gm.listeners.size() - 1; i >= 0; i--) {
- ( (UserActionListener) gm.listeners.get(i)).handleHangupRequest(
- commEvt);
- }
- }
- }
- public void answer()
- {
- InterlocutorUI inter = gm.interlocutors.getInterlocutorAt(0);
- if(inter!=null) {
- UserCallControlEvent commEvt = new UserCallControlEvent(inter);
- for (int i = gm.listeners.size() - 1; i >= 0; i--) {
- ( (UserActionListener) gm.listeners.get(i)).handleAnswerRequest(
- commEvt);
- }
- }
- }
- public void mute()
- {
- InterlocutorUI inter = gm.interlocutors.getInterlocutorAt(0);
- if(inter!=null) {
- UserCallControlEvent commEvt = new UserCallControlEvent(inter);
- for (int i = gm.listeners.size() - 1; i >= 0; i--) {
- ( (UserActionListener) gm.listeners.get(i)).handleMuteRequest(
- commEvt);
- }
- }
- }
- }