FluorineFx + Flex视频聊天室案例开发----客户端
上一篇《FluorineFx + Flex视频聊天室案例开发----服务器端》详细的介绍了如何利用FluorineFx开发一个及时通信的视频聊天室服务器处理程序,并通过Web网站来宿主这个服务处理程序的运行。本篇将着重介绍视频聊天室的客户端开发,包括连接RTMP服务器、发布视频、接收视频、在线用户列表、发送文本消息以及全服务器小喇叭功能点。
上述这些功能点在我以前写的文章里已经出现了N多次了,所以这里我不想过多的在次对他们进行解说,详细请查阅《Flex与.NET互操作系列文章 》,这里我将核心的几个方法代码贴出来简单说明。首先就是客户实现用户登录,通过FluorineFx提供的RemotingService的接口方法进行数据验证。
{
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 = " 登陆失败,用户名或密码错误! " ;
}
}
private function onLoginFault( event :Object): void
{
this .lbState.text = " 登陆失败,请重试! " ;
}
private function onClear( event :MouseEvent): void
{
this .txtUserName.text = "" ;
this .txtPassword.text = "" ;
this .lbState.text = "" ;
this .txtUserName.setFocus();
}
RemotingConnection和RtmpConnection是我自己扩展的NetConnection类,功能和NetConnection一样,不同的是封装后的使用相对来说比较方便点。首先通过RemotingService的接口进行用户名和密码验证,通过了则创建一个到RTMP服务器的连接RtmpConnection(等同于NetConnection)。
{
trace( event .info.code);
switch ( event .info.code)
{
case " NetConnection.Connect.Success " :onConnSuccess(); break ;
case " NetConnection.Connect.Failed " :onConnError(); break ;
}
}
private function onConnSuccess(): void
{
// 将自己的视频数据发布到RTMP服务器,这里使用的是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作为流名进行发布实况流
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\ " >系统提示:连接视频服务器失败</font> " );
}
创建连接的同时指定了由那一个方法(onNetStatusHandler)来处理连接状态,通过判断连接状态如果连接成功则将自己的视频数据发布到RTMP服务器(特别提醒:在发布流的时候是使用的用户ID作为流名,在建立视频聊天的时候需要根据这个ID才能查看到视频),同时还连接到服务器上的远程共享对象(作用:通过异步事件处理函数实现在线用户列表),最后建立了一个Timer是不断的调用服务器方法获取当前系统时间(注意:实际开发中不建议这样做);如果连接服务器失败则在聊天消息显示区输入一条提示信息。
在线用户列表使用共享对象来实现,可以及时的处理用户上线下线功能和实现客户端数据同步更新等。下面是共享对象的异步事件处理函数:
{
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);
}
}
从异步事件中取出当前最新的数据,然后添加到用户界面的显示列表数组(userArray)里,Flex直接使用List组件显示在线用户列表,通过绑定userArray设置数据源,当userArray改变后List组件的显示也会同步更新显示。
那么怎么去建立视频聊天查看到对方的视频呢?其实实现也很简单,这里还是要从用户列表出发,通过点击用户列表上的在线用户,然后建立与该用户的视频连接。同时判断是否选择的是怎么,本案例中我没有将自己从在线列表里屏蔽而是通过判断当前选择的是否为自己,如果是自己则不进行视频连接,也不能发送文本聊天信息。
{
info = List( event .target).selectedItem as UserInfo; //把当前选择的用户信息通过变量保存下来
this .lbNickName.text = info.NickName;
if (info.UserName == myInfo.UserName)
{
writeMessage( " <font color=\ " #FF0000\ " >系统提示:不能和自己进行视频聊天</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()); //当前选择的用户的ID
}
}
OK,到这里就成功的完成了用户登录,建立与RTMP服务器的连接,发布视频流,接收指定的视频流等功能,接下来就是实现文字聊天的功能了。实现文字聊天功能是最简单的,我曾经先后在《FMS3系列(六):使用远程共享对象(SharedObject)实现多人时时在线聊天(Flex | Flash) 》和《Flex与.NET互操作(十二):FluorineFx.Net的及时通信应用(Remote Shared Objects)(三) 》这两篇文章中都介绍到了,这里我使用的是第二篇文章里所介绍的方法(提示:该方法就是直接使用SharedObject的send()方法)来实现文字聊天功能。
{
if (info != null )
{
userSO.send( " chatMessage " , this .txtMessage.text, myInfo, info);
this .txtMessage.text = "" ;
}
else
{
writeMessage( " 系统提示:请选择聊天对象 " );
}
}
public function chatMessage(message:String, sayUser:UserInfo, recUser:UserInfo): void
{
if (recUser.UserName == this .myInfo.UserName)
{
message = sayUser.NickName + " 对你说: " + message;
writeMessage(message);
}
if (sayUser.UserName == this .myInfo.UserName)
{
message = " 我对 " + recUser.NickName + " 说: " + message;
writeMessage(message);
}
}
private function writeMessage(message:String): void
{
this .txtDisMessage.htmlText += message + " \n " ;
this .txtDisMessage.verticalScrollPosition = this .txtDisMessage.maxVerticalScrollPosition;
}
OK,大功告成,现在是集视频和文字聊天的多人在线聊天室就实现了,不足的是只能一对一聊天。如果我要对大家说话怎么办呢?于是我在本案例中设计了一个小喇叭功能,通过发送小喇叭实现全服务器喊话。下边是下喇叭组件代码:
< 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 = " 关闭 " click = " onClose(event) " />
< mx:Button x = " 410 " y = " 3 " label = " 发送 "
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 >
同样通过远程共享对象的send()方法实现发送小喇叭功能,在客户端定义一个方法(speakMessage)来接受小喇叭发送的消息内容,然后显示在用户聊天界面上。
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 + " 说: " + message;
writeMessage(message);
}
貌似这一整篇都是代码,除了代码我也不知道该怎么去介绍更容易说得清楚了,下面来看看上面的劳动成功,启动服务器后运行多个客户端来聊天测试看看。
现在还差一个重要的功能没有实现了,前面提到过画中画功能,也就是说在和在线朋友进行视频聊天的同时,需要将自己的视频以小视频窗口的方式显示在聊天窗口,实现所谓的画中画功能,显示自己的视频通过初始化方法,程序启动后就直接显示出自己的视频。
{
// 将自己的视频显示在画中画中
cam = Camera.getCamera();
if (cam != null )
{
this .myVD.attachCamera(cam);
}
else
{
writeMessage( " 未能找到视频设备,请检测是否正确安装设备! " );
}
}
本文就介绍到这里,关于聊天表情的实现这里就不作介绍了,由于时间关系本案例里也没有实现这个功能,有兴趣的朋友可以下载源代码自己去扩展实现聊天表情这个功能。这里我将实现的原理简单说一下,通过TileList组件加载表情图片或动画信息显示出来,详细可以参考《使用TileList+TitleWindow组件开发聊天表情功能 》,发送表情则是将图片地址通过SharedObject的send()方法发送出去,接收消息的方法通过图片地址,组合<img src='图片地址' />然后显示在聊天信息窗口中。