FluorineFx + Flex视频聊天室案例开发----客户端
??????上一篇《FluorineFx + Flex視頻聊天室案例開(kāi)發(fā)----服務(wù)器端》詳細(xì)的介紹了如何利用FluorineFx開(kāi)發(fā)一個(gè)及時(shí)通信的視頻聊天室服務(wù)器處理程序,并通過(guò)Web網(wǎng)站來(lái)宿主這個(gè)服務(wù)處理程序的運(yùn)行。本篇將著重介紹視頻聊天室的客戶端開(kāi)發(fā),包括連接RTMP服務(wù)器、發(fā)布視頻、接收視頻、在線用戶列表、發(fā)送文本消息以及全服務(wù)器小喇叭功能點(diǎn)。
??????上述這些功能點(diǎn)在我以前寫(xiě)的文章里已經(jīng)出現(xiàn)了N多次了,所以這里我不想過(guò)多的在次對(duì)他們進(jìn)行解說(shuō),詳細(xì)請(qǐng)查閱《Flex與.NET互操作系列文章 》,這里我將核心的幾個(gè)方法代碼貼出來(lái)簡(jiǎn)單說(shuō)明。首先就是客戶實(shí)現(xiàn)用戶登錄,通過(guò)FluorineFx提供的RemotingService的接口方法進(jìn)行數(shù)據(jù)驗(yàn)證。
private?function?onLogin(event:MouseEvent):void{
????remoteConn?=?new?RemotingConnection("http://localhost:2020/ChatRoom.FluorineFxWeb/Gateway.aspx",ObjectEncoding.AMF3);
????myInfo?=?new?UserInfo();
????myInfo.UserName=this.txtUserName.text;
????myInfo.Password=this.txtPassword.text;
????remoteConn.RemotingCall("ChatRoom.Services.DataService.Login",onLoginResult,onLoginFault,myInfo);
}
private?function?onLoginResult(result:UserInfo):void
{
????if(result?!=?null)
????{
????????this.myInfo?=?result;
????????this.viewStack.selectedChild?=?chatView;
????????rtmpnc?=?new?RtmpConnection("rtmp://localhost:2777/VideoChat",ObjectEncoding.AMF3,onNetStatusHandler,myInfo);
????}
????else
????{
????????this.lbState.text?=?"登陸失敗,用戶名或密碼錯(cuò)誤!";
????}
}
private?function?onLoginFault(event:Object):void
{
????this.lbState.text?=?"登陸失敗,請(qǐng)重試!";
}
private?function?onClear(event:MouseEvent):void
{
????this.txtUserName.text="";
????this.txtPassword.text="";
????this.lbState.text="";
????this.txtUserName.setFocus();
}
?
??????RemotingConnection和RtmpConnection是我自己擴(kuò)展的NetConnection類,功能和NetConnection一樣,不同的是封裝后的使用相對(duì)來(lái)說(shuō)比較方便點(diǎn)。首先通過(guò)RemotingService的接口進(jìn)行用戶名和密碼驗(yàn)證,通過(guò)了則創(chuàng)建一個(gè)到RTMP服務(wù)器的連接RtmpConnection(等同于NetConnection)。
private?function?onNetStatusHandler(event:NetStatusEvent):void{
????trace(event.info.code);
????switch(event.info.code)
????{
????????case?"NetConnection.Connect.Success":onConnSuccess();break;
????????case?"NetConnection.Connect.Failed":onConnError();break;
????}
}
private?function?onConnSuccess():void
{
????//將自己的視頻數(shù)據(jù)發(fā)布到RTMP服務(wù)器,這里使用的是FluorineFx
????var?mic:Microphone?=?Microphone.getMicrophone();
????var?publishNs:NetStream?=?new?NetStream(rtmpnc);
????publishNs.attachCamera(cam);
????publishNs.attachAudio(mic);
????publishNs.client?=?this;
????publishNs.publish(myInfo.ID.toString());?//將用戶ID作為流名進(jìn)行發(fā)布實(shí)況流
????
????userSO?=?SharedObject.getRemote("OnLineUsers",rtmpnc.uri,false);
????userSO.addEventListener(SyncEvent.SYNC,onSyncHandler);
????userSO.client?=?this;
????userSO.connect(rtmpnc);
????
????timer?=?new?Timer(1000);
????timer.addEventListener(TimerEvent.TIMER,onTimerHandler);
????timer.start();
}
private?function?onConnError():void
{
????trace("login?error");
????writeMessage("<font?color=\"#FF0000\">系統(tǒng)提示:連接視頻服務(wù)器失敗</font>");
}
?
??????創(chuàng)建連接的同時(shí)指定了由那一個(gè)方法(onNetStatusHandler)來(lái)處理連接狀態(tài),通過(guò)判斷連接狀態(tài)如果連接成功則將自己的視頻數(shù)據(jù)發(fā)布到RTMP服務(wù)器(特別提醒:在發(fā)布流的時(shí)候是使用的用戶ID作為流名,在建立視頻聊天的時(shí)候需要根據(jù)這個(gè)ID才能查看到視頻),同時(shí)還連接到服務(wù)器上的遠(yuǎn)程共享對(duì)象(作用:通過(guò)異步事件處理函數(shù)實(shí)現(xiàn)在線用戶列表),最后建立了一個(gè)Timer是不斷的調(diào)用服務(wù)器方法獲取當(dāng)前系統(tǒng)時(shí)間(注意:實(shí)際開(kāi)發(fā)中不建議這樣做);如果連接服務(wù)器失敗則在聊天消息顯示區(qū)輸入一條提示信息。
??????在線用戶列表使用共享對(duì)象來(lái)實(shí)現(xiàn),可以及時(shí)的處理用戶上線下線功能和實(shí)現(xiàn)客戶端數(shù)據(jù)同步更新等。下面是共享對(duì)象的異步事件處理函數(shù):
private?function?onSyncHandler(event:SyncEvent):void{
????var?array:Array?=?event.target.data.UserInfo?as?Array;
????if(array?!=?null)
????{
????????userArray.removeAll();
????????for(var?i:Number=0;?i<array.length;?i++)
????????{
????????????var?info:UserInfo?=?array[i]?as?UserInfo;
????????????userArray.addItem(info);
????????}
????????trace("userArray?length:"?+?userArray.length);
????}
}
?
??????從異步事件中取出當(dāng)前最新的數(shù)據(jù),然后添加到用戶界面的顯示列表數(shù)組(userArray)里,Flex直接使用List組件顯示在線用戶列表,通過(guò)綁定userArray設(shè)置數(shù)據(jù)源,當(dāng)userArray改變后List組件的顯示也會(huì)同步更新顯示。
???????那么怎么去建立視頻聊天查看到對(duì)方的視頻呢?其實(shí)實(shí)現(xiàn)也很簡(jiǎn)單,這里還是要從用戶列表出發(fā),通過(guò)點(diǎn)擊用戶列表上的在線用戶,然后建立與該用戶的視頻連接。同時(shí)判斷是否選擇的是怎么,本案例中我沒(méi)有將自己從在線列表里屏蔽而是通過(guò)判斷當(dāng)前選擇的是否為自己,如果是自己則不進(jìn)行視頻連接,也不能發(fā)送文本聊天信息。
private?function?onUserItemHandler(event:Event):void{
????info?=?List(event.target).selectedItem?as?UserInfo;? //把當(dāng)前選擇的用戶信息通過(guò)變量保存下來(lái)
????this.lbNickName.text?=?info.NickName;
????
????if(info.UserName?==?myInfo.UserName)
????{
????????writeMessage("<font?color=\"#FF0000\">系統(tǒng)提示:不能和自己進(jìn)行視頻聊天</font>");
????}
????else
????{
????????//建立視頻流的連接
????????if(this.ns)
????????{
????????????this.ns.close();
????????}
????????this.ns?=?new?NetStream(this.rtmpnc);
????????ns.client?=?this;
????????sound?=?this.ns.soundTransform;
????????var?v1:Video?=?new?Video();
????????v1.width?=?320;
????????v1.height?=?240;
????????v1.attachNetStream(ns);
????????this.videoDisplay.addChild(v1);
????????ns.play(info.ID.toString());? //當(dāng)前選擇的用戶的ID
????}
}
?
??????OK,到這里就成功的完成了用戶登錄,建立與RTMP服務(wù)器的連接,發(fā)布視頻流,接收指定的視頻流等功能,接下來(lái)就是實(shí)現(xiàn)文字聊天的功能了。實(shí)現(xiàn)文字聊天功能是最簡(jiǎn)單的,我曾經(jīng)先后在《FMS3系列(六):使用遠(yuǎn)程共享對(duì)象(SharedObject)實(shí)現(xiàn)多人時(shí)時(shí)在線聊天(Flex | Flash) 》和《Flex與.NET互操作(十二):FluorineFx.Net的及時(shí)通信應(yīng)用(Remote Shared Objects)(三) 》這兩篇文章中都介紹到了,這里我使用的是第二篇文章里所介紹的方法(提示:該方法就是直接使用SharedObject的send()方法)來(lái)實(shí)現(xiàn)文字聊天功能。
?
private?function?onSendMessage(event:MouseEvent):void{
????if(info!=null)
????{
????????userSO.send("chatMessage",?this.txtMessage.text,?myInfo,?info);
????????this.txtMessage.text="";
????}
????else
????{
????????writeMessage("系統(tǒng)提示:請(qǐng)選擇聊天對(duì)象");
????}
}
public?function?chatMessage(message:String,?sayUser:UserInfo,?recUser:UserInfo):void
{
????if(recUser.UserName==this.myInfo.UserName)
????{
????????message?=?sayUser.NickName?+?"對(duì)你說(shuō):"+message;
????????writeMessage(message);
????}
????if(sayUser.UserName==this.myInfo.UserName)
????{
????????message?=?"我對(duì)"+recUser.NickName?+?"說(shuō):"+message;
????????writeMessage(message);
????}
}
private?function?writeMessage(message:String):void
{
????this.txtDisMessage.htmlText?+=?message?+?"\n";
????this.txtDisMessage.verticalScrollPosition?=?this.txtDisMessage.maxVerticalScrollPosition;
}
?
??????OK,大功告成,現(xiàn)在是集視頻和文字聊天的多人在線聊天室就實(shí)現(xiàn)了,不足的是只能一對(duì)一聊天。如果我要對(duì)大家說(shuō)話怎么辦呢?于是我在本案例中設(shè)計(jì)了一個(gè)小喇叭功能,通過(guò)發(fā)送小喇叭實(shí)現(xiàn)全服務(wù)器喊話。下邊是下喇叭組件代碼:
<?xml?version="1.0"?encoding="utf-8"?><mx:TitleWindow?xmlns:mx="http://www.adobe.com/2006/mxml"?layout="absolute"?width="540"?height="50"?
????headerHeight="8"?roundedBottomCorners="true"?borderColor="#000000">
????<mx:TextInput?x="2"?y="3"?width="400"?id="txtMessage"/>
????<mx:Button?x="466"?y="3"?label="關(guān)閉"?click="onClose(event)"/>
????<mx:Button?x="410"?y="3"?label="發(fā)送"?
????????enabled="{txtMessage.text.length?>?0???true?:?false}"?
????????click="onSend(event)"/>
????
????<mx:Script>
????????<![CDATA[
????????????import?mx.core.Application;
????????????import?mx.events.CloseEvent;
????????????import?mx.managers.PopUpManager;
????????????private?function?init():void
????????????{
????????????????this.txtMessage.setFocus();
????????????}
????????????
????????????private?function?onClose(event:MouseEvent):void
????????????{
????????????????onCloseHandler(null);
????????????}
????????????
????????????private?function?onCloseHandler(event:CloseEvent):void
????????????{
????????????????Application.application.speakFlag?=?false;
????????????????PopUpManager.removePopUp(this);
????????????}
????????????
????????????private?function?onSend(event:MouseEvent):void
????????????{
????????????????Application.application.userSO.send("speakMessage",txtMessage.text,Application.application.myInfo);
????????????????this.txtMessage.text?=?"";
????????????????onCloseHandler(null);
????????????}
????????]]>
????</mx:Script>
</mx:TitleWindow>
??????同樣通過(guò)遠(yuǎn)程共享對(duì)象的send()方法實(shí)現(xiàn)發(fā)送小喇叭功能,在客戶端定義一個(gè)方法(speakMessage)來(lái)接受小喇叭發(fā)送的消息內(nèi)容,然后顯示在用戶聊天界面上。
private?function?onSpeaker(event:MouseEvent):void
{
????if(!speakFlag)
????{
????????var?dis:Speaker?=?new?Speaker();
????????dis.x?=?230;
????????dis.y?=?505;
????????PopUpManager.addPopUp(dis,this,false);
????????speakFlag?=?true;
????}
}
public?function?speakMessage(message:String,info:UserInfo):void
{
????message?=?"【小喇叭】:"?+?info.NickName?+?"說(shuō):"?+?message;
????writeMessage(message);?
}
?
??????貌似這一整篇都是代碼,除了代碼我也不知道該怎么去介紹更容易說(shuō)得清楚了,下面來(lái)看看上面的勞動(dòng)成功,啟動(dòng)服務(wù)器后運(yùn)行多個(gè)客戶端來(lái)聊天測(cè)試看看。????????????
????????????
????????????
????????????
?
??????現(xiàn)在還差一個(gè)重要的功能沒(méi)有實(shí)現(xiàn)了,前面提到過(guò)畫(huà)中畫(huà)功能,也就是說(shuō)在和在線朋友進(jìn)行視頻聊天的同時(shí),需要將自己的視頻以小視頻窗口的方式顯示在聊天窗口,實(shí)現(xiàn)所謂的畫(huà)中畫(huà)功能,顯示自己的視頻通過(guò)初始化方法,程序啟動(dòng)后就直接顯示出自己的視頻。
private?function?init():void{
????//將自己的視頻顯示在畫(huà)中畫(huà)中
????cam?=?Camera.getCamera();
????if(cam?!=?null)
????{
????????this.myVD.attachCamera(cam);
????}
????else
????{
????????writeMessage("未能找到視頻設(shè)備,請(qǐng)檢測(cè)是否正確安裝設(shè)備!");
????}
}
?
??????本文就介紹到這里,關(guān)于聊天表情的實(shí)現(xiàn)這里就不作介紹了,由于時(shí)間關(guān)系本案例里也沒(méi)有實(shí)現(xiàn)這個(gè)功能,有興趣的朋友可以下載源代碼自己去擴(kuò)展實(shí)現(xiàn)聊天表情這個(gè)功能。這里我將實(shí)現(xiàn)的原理簡(jiǎn)單說(shuō)一下,通過(guò)TileList組件加載表情圖片或動(dòng)畫(huà)信息顯示出來(lái),詳細(xì)可以參考《使用TileList+TitleWindow組件開(kāi)發(fā)聊天表情功能 》,發(fā)送表情則是將圖片地址通過(guò)SharedObject的send()方法發(fā)送出去,接收消息的方法通過(guò)圖片地址,組合<img src='圖片地址' />然后顯示在聊天信息窗口中。
?
版權(quán)說(shuō)明
? 本文屬原創(chuàng)文章,歡迎轉(zhuǎn)載,其版權(quán)歸作者和博客園共有。??
? 作??????者:Beniao
?文章出處:http://beniao.cnblogs.com/? 或? http://www.cnblogs.com/
?
轉(zhuǎn)載于:https://www.cnblogs.com/beniao/archive/2009/06/29/1511813.html
總結(jié)
以上是生活随笔為你收集整理的FluorineFx + Flex视频聊天室案例开发----客户端的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
                            
                        - 上一篇: 在WinForm中通过HTTP协议向服务
 - 下一篇: 转:关于CCDISK的优化