很多交友网站都具体在线视频认证能,如51.com。参考它的认证流程,基于Flex结合Flash Media Server开发一个自己的认证功能。在编写代码这前我们先来了解我们要实现的功能,见效果图:
(1)客户视频的截图
下面我们将分步讲解具体实现过程:
(一)客户视频截图:
常来访问我的博客且使用Flex的朋友也许会发现,以前我发布的一篇《Flex视频截图并通过WebService(C#)保存》。您猜对了,正是用这种方法实现客户视频载图,在些就不再重述了。
(二)添加FMS应用
安装Flash Midea Server 2,在安装路径的Flash Midea Server 2\applications下建立test文夹件(文件夹名称自己定义),重新启动FMS。
(三)客户视频认证准备
主要实现FMS在线共享视频和接收FMS传送的认证结果信息。详见代码:
<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” creationComplete=”initApp()” width=”214″ height=”214″>
<mx:Style>
Alert{font-size:12px;}
</mx:Style>
<mx:Script>
<![CDATA[
import mx.events.CloseEvent;
import mx.controls.Alert;
import flash.net.navigateToURL;
//由于fms使用的是amf0而flex3中的as3默认使用的是amf3.所以要让flex使用AFM0
NetConnection.defaultObjectEncoding = ObjectEncoding.AMF0;
private static const DEFAULT_WIDTH:Number = 160; //摄像头显示高度
private static const DEFAULT_HEIGHT:Number = 120; //摄像头显示高度
private var m_camera:Camera; //定义一个摄像头
private var m_microphone:Microphone; //定义一个麦克风
private var m_localVideo:Video; //定义一个本地视频
private var m_netConnection:NetConnection; //定认一个网络连接
private var m_outStream:NetStream; //定义一个输出流
private var m_fmsServer:String; //视频服务器地址
private var m_userId:String; //用户ID
private function initApp():void
{
m_fmsServer = "rtmp://192.168.0.107/test";
//m_fmsServer = Application.application.parameters.videoserver;
m_userId = "1054";
//m_userId = Application.application.parameters.userid;
initCamera();
}
//初始化摄像头
private function initCamera():void
{
m_camera = Camera.getCamera();
if(m_camera != null)
{
m_camera.addEventListener(StatusEvent.STATUS,__onCameraStatusHandler);
m_camera.setMode(DEFAULT_WIDTH,DEFAULT_HEIGHT,30);
m_localVideo = new Video();
m_localVideo.width = DEFAULT_WIDTH;
m_localVideo.height = DEFAULT_HEIGHT;
m_localVideo.attachCamera(m_camera);
t_vd_Video.addChild(m_localVideo);
}
else
{
Alert.show("没有找到摄像头,是否重新查找。","提示",Alert.OK|Alert.NO,this,_initCamera);
return;
}
}
//连接网络
private function checkNetConnection():void
{
m_netConnection = new NetConnection();
m_netConnection.addEventListener(NetStatusEvent.NET_STATUS,__onNetStatusHandler);
m_netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR,__onSecurityErrorHandler);
m_netConnection.connect(m_fmsServer,m_userId);
m_netConnection.client = this;
}
//共享视频流
private function shareVideoStreame():void
{
m_outStream = new NetStream(m_netConnection);
m_outStream.attachCamera(m_camera);
m_outStream.attachAudio(m_microphone);
m_outStream.publish(m_userId,"live");
}
//检测掇像头权限事件
private function __onCameraStatusHandler(event:StatusEvent):void
{
if(!m_camera.muted)
{
checkNetConnection();
}else
{
Alert.show("无法接连接到活动摄像头,是否重新检测。","提示",Alert.OK|Alert.NO,this,_initCamera);
}
m_camera.removeEventListener(StatusEvent.STATUS,__onCameraStatusHandler);
}
//当摄像头不存在,或连接不正常时重新获取
private function _initCamera(event:CloseEvent):void
{
if(event.detail == Alert.OK)
{
initCamera();
}
}
//网络连接事件
//如果网络连接成功,开始共享视频
private function __onNetStatusHandler(event:NetStatusEvent):void
{
switch(event.info.code)
{
case "NetConnection.Connect.Success":
shareVideoStreame();
t_lbl_Info.visible = true;
break;
case "NetConnection.Connect.Failed":
Alert.show("网络连接失败,请稍后再试。","提示",Alert.OK);
break;
case "NetConnection.Connect.Rejected":
Alert.show("遭到服务器拒绝,请稍后再试。","提示",Alert.OK);
break;
}
}
private function __onSecurityErrorHandler(event:SecurityErrorEvent):void
{
trace("securityErrorHandler:" + event);
}
//接收服务端IM信息
public function AcceptServerMessage(m_result:String):void
{
t_lbl_Info.text = m_result;
}
//接收验证结果信息
public function ServerValidateMessage(m_isPass:Boolean,m_result:String):void
{
if(m_isPass)
{
Alert.show(m_result,"提示",Alert.OK,this,validateSuccessHandler);
}
else
{
Alert.show(m_result,"提示",Alert.OK|Alert.NO,this,validateFaildHandler);
}
m_netConnection.close();
}
//视频验证成功确认事件
private function validateSuccessHandler(event:CloseEvent):void
{
if(event.detail == Alert.OK)
{
//trace("页面转向");
var m_url:URLRequest = new URLRequest("http://www.soave.cn");
navigateToURL(m_url,"_self");
}
}
//视频验证失败确认事件
private function validateFaildHandler(event:CloseEvent):void
{
var m_url:URLRequest;
if(event.detail == Alert.OK)
{
//trace("页面转向,再次进行认证。");
m_url = new URLRequest("http://www.soave.cn");
navigateToURL(m_url,"_self");
}
else
{
//trace("页面转向,取消认证。");
m_url = new URLRequest("http://www.soave.cn");
navigateToURL(m_url,"_self");
}
}
]]>
</mx:Script>
<mx:Panel x=”16″ y=”16″ width=”180″ height=”162″ layout=”absolute” title=”在线认证视频” fontSize=”12″>
<mx:VideoDisplay id=”t_vd_Video” width=”160″ height=”120″/>
</mx:Panel>
<mx:Label id=”t_lbl_Info” x=”14″ y=”184″ text=”视频认证就绪” fontSize=”12″ color=”#FFFFFF”/>
</mx:Application>
checkNetConnection()连接网络方法中
程序代码
m_netConnection.client = this;
定义FMS回调的客户端,当FMS中的main.asc需要调用客户端中的方法时,一定要指定。
shareVideoStreame()共享视频方法中
程序代码
m_outStream.publish(m_userId,”live”);
live表示共享视频流,而不保存,当为record时,则保存视频。
AcceptServerMessage (m_result:String)和ServerValidateMessage(m_isPass:Boolean,m_result: String)方法都是在FMS中的main.asc调用。后面讲解到main.asc时,我们再详细解释。
(四)定义服务端脚本main.asc
新建一个main.asc,保存在安装路径的Flash Midea Server 2\applications\test中。
main.asc代码:
程序代码
application.onAppStart = function()
{
userList_so = SharedObject.get(”userList”,”false”);
}
application.onConnect = function(client,userID)
{
client.userid = userID;
userList_so.setProperty (userID,userID);
return true;
}
application.onDisconnect = function(client)
{
userList_so.setProperty(client.userid,null);
}
Client.prototype.SendMessage = function(userID,sendMsg)
{
for(var i=0;i<application.clients.length;i++)
{
if(application.clients[i].userid == userID)
{
application.clients[i].call(”AcceptServerMessage”,null,sendMsg);
}
}
return “信息发送成功!”;
}
Client.prototype.ValidateUser = function(userID,isPass,sendMsg)
{
for(var i=0;i<application.clients.length;i++)
{
if(application.clients[i].userid == userID)
{
application.clients[i].call(”ServerValidateMessage”,null,isPass,sendMsg);
}
}
return “验证结果信息发送成功!”;
}
Client.prototype.DeleteInvalidUser = function(userID)
{
for(var i=0;i<application.clients.length;i++)
{
if(application.clients[i].userid == userID)
{
application.clearStreams(application.clients[i]);
application.disconnect(application.clients[i]);
}
}
return ”成功踢出无效连接!”;
}
application.onAppStart和application.onConnect方法定义SharedObject对象userList_so,userList_so保存着所有连接进来的客户端信息。
SendMessage 方法向客户端发送即时信息,此方法在后台认证操作管理中调用。参数userID表示要发送的客户端ID,参数sendMsg表示发送的信息内容。
ValidateUser 方法向后台认证操作管理中已进行认证操作的客户端传送认证结果信息,调用客户端准备程序中的ServerValidateMessage方法,参数 userID表示要发送的客户端ID,isPass表示是否通过认证,true表示已通过,false表示被拒绝,参数sendMsg表示发送的信息内容。
DeleteInvalidUser 方法踢除无效的连接。参数userID表示要发送的客户端ID。
(五)后台认证操作管理
主要实现连接选定的客户端共享视频和截图、显示FMS的连接用户列表、传送即时信息到指定的客户端、踢掉连接到FMS的某个用户等。详见代码:
程序代码
<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” width=”394″ height=”488″ creationComplete=”initApp()”>
<mx:Style>
Panel,Button,TextArea{font-size:12px;}
</mx:Style>
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
//由于fms使用的是amf0而flex3中的as3默认使用的是amf3.所以要让flex使用AFM0
NetConnection.defaultObjectEncoding= flash.net.ObjectEncoding.AMF0;
SharedObject.defaultObjectEncoding = flash.net.ObjectEncoding.AMF0;
private static const VIDEO_WIDTH:int = 160; //视频默认宽度
private static const VIDEO_HEIGHT:int = 120; //视频默认高度
private var videoServer:String; //视频服务器地址
[Bindable]
private var videoName:String; //视频名称
private var netConn:NetConnection;
private var inStream:NetStream;
private var sharedObject:SharedObject;
private var callResponser:Responder;
[Bindable]
private var isPass:Boolean; //用户是否通过认证标识
private function initApp():void
{
videoServer = “rtmp://192.168.0.107/test”;
//videoServer = Application.application.parameters.videoserver;
callResponser = new Responder(GetCallResult);
CheckNetConnection();
}
//检测网络连接
private function CheckNetConnection():void
{
netConn = new NetConnection();
netConn.addEventListener(NetStatusEvent.NET_STATUS,__onStatusHandler);
netConn.addEventListener(SecurityErrorEvent.SECURITY_ERROR,__onSecurityErrorHandler);
netConn.connect(videoServer);
netConn.client = this;
}
//获取视频流
private function GetVideoStream():void
{
inStream = new NetStream(netConn);
inStream.addEventListener(NetStatusEvent.NET_STATUS,__onStatusHandler);
inStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR,__onAsyncErrorHandler);
//定义onMetaData,获取视频相关数据
var m_CustomerClient:Object = new Object()
m_CustomerClient.onMetaData = function(metadata:Object):void{}
inStream.client = m_CustomerClient;
var m_Video:Video = new Video();
m_Video.attachNetStream(inStream);
m_Video.width = VIDEO_WIDTH;
m_Video.height = VIDEO_HEIGHT;
inStream.play(videoName);
t_vd_Video.addChild(m_Video);
}
//获取在线认证用户列表共享对像
private function GetUserListSharedObject():void
{
//netConn.call(”UserListFromClient”,callResponser);
sharedObject = SharedObject.getRemote(”userList”,netConn.uri,false);
sharedObject.addEventListener(SyncEvent.SYNC,__onSyncHandler);
sharedObject.connect(netConn);
t_list_User.dataProvider = new ArrayCollection();
}
//获取在线认证用户列表
private function GetUserList():void
{
var m_ac:ArrayCollection = t_list_User.dataProvider as ArrayCollection;
m_ac.removeAll();
for(var i:String in sharedObject.data)
{
var item:Object = {userid:i,data:i};
m_ac.addItem(item);
}
t_list_User.dataProvider = m_ac;
//当前用户通过认证后自动显示下一用户
if(m_ac.length > 0)
{
videoName = m_ac[0].data;
t_ta_AcceptText.text = “”;
GetUserPicture();
GetVideoStream();
}
else
{
videoName = null;
}
}
//认证通过和认证拒绝按钮事件
private function SetUserPass():void
{
if(videoName != null)
{
//从SharedObject中删除该用户并发送认证结果信息
var m_Msg:String = “”;
if(isPass)
{
m_Msg = “恭喜您,通过视频认证。”;
}
else
{
m_Msg = “您没有通过视频认证,\n是否重新认证?”;
}
netConn.call(”ValidateUser”,callResponser,videoName,isPass,m_Msg);
}
}
//删除无效的连接用户
private function DeleteInvalidUser():void
{
if(videoName != null)
{
netConn.call(”DeleteInvalidUser”,callResponser,videoName);
}
}
//当点击t_list_User中的用户时,获取该用户的视频和图片
private function GetUserVideoAndImg():void
{
videoName=t_list_User.selectedItem.data;
t_btn_Send.enabled = true;
t_ta_AcceptText.text = “”;
GetUserPicture();
GetVideoStream();
}
//获取用户视频截图
private function GetUserPicture():void
{
t_img_Picture.source = “xxxxx.jpg”;
//可以自定义方法获取视频截图
}
//网络连接事件
private function __onStatusHandler(event:NetStatusEvent):void
{
switch(event.info.code)
{
case “NetConnection.Connect.Success”:
GetUserListSharedObject();
break;
case “NetConnection.Connect.Failed”:
Alert.show(”网络连接失败,请稍后重试。”,”提示”,Alert.OK);
break;
case “NetStream.Play.StreamNotFound”:
Alert.show(”视频文件不存在,请稍后重试。”,”提示”,Alert.OK);
break;
}
}
private function __onSecurityErrorHandler(event:SecurityErrorEvent):void
{
Alert.show(”服务器发生错误:”+ event.text,”提示”,Alert.OK);
}
//共享对象改变事件
//当共享对角改变时,重新获取用户列表
private function __onSyncHandler(event:SyncEvent):void
{
GetUserList();
}
private function __onAsyncErrorHandler(event:AsyncErrorEvent):void
{
trace(event.error.message);
}
//发送聊天信息按钮事件
//向服务器传送信息,与当前正在视频验证的用户通讯
private function SendMessage():void
{
if(t_ta_SendText.text != “”)
{
if(videoName != null)
{
netConn.call(”SendMessage”,callResponser,videoName,t_ta_SendText.text);
}
t_ta_AcceptText.text += “我:” + t_ta_SendText.text + “\n”;
t_ta_SendText.text = “”;
focusManager.setFocus(t_ta_SendText);
}
}
//与FMS服务器通讯结果
private function GetCallResult(m_Result:String):void
{
trace(”从服务器返回的信息:” + m_Result);
}
]]>
</mx:Script>
<mx:Panel x=”10″ y=”10″ width=”180″ height=”200″ layout=”absolute” title=”{videoName}认证视频”>
<mx:VideoDisplay id=”t_vd_Video” width=”160″ height=”120″/>
<mx:ControlBar horizontalAlign=”right”>
<mx:Button id=”t_btn_Pass” label=”通过认证” click=”{isPass = true;SetUserPass();}”/>
<mx:Button id=”t_btn_Refuse” label=”拒绝” click=”{isPass = false;SetUserPass();}”/>
</mx:ControlBar>
</mx:Panel>
<mx:Panel x=”204″ y=”10″ width=”180″ height=”200″ layout=”absolute” title=”{videoName}认证截图”>
<mx:Image id=”t_img_Picture” width=”160″ height=”120″/>
<mx:ControlBar horizontalAlign=”right”>
<mx:Button id=”t_btn_GetPicture” label=”重新获取” click=”GetUserPicture()” />
</mx:ControlBar>
</mx:Panel>
<mx:Canvas x=”10″ y=”218″>
<mx:List id=”t_list_User” x=”0″ y=”0″ width=”180″ height=”228″ labelField=”userid” itemClick=”GetUserVideoAndImg()”></mx:List>
<mx:Button id=”t_btn_Disconn” x=”0″ y=”236″ label=”踢出” click=”DeleteInvalidUser()”/>
<mx:Button id=”t_btn_UserList” x=”76″ y=”236″ label=”刷新用户列表” click=”GetUserList()”/>
</mx:Canvas>
<mx:Canvas x=”204″ y=”218″>
<mx:TextArea id=”t_ta_AcceptText” x=”0″ y=”0″ width=”180″ height=”182″ editable=”false”/>
<mx:TextArea id=”t_ta_SendText” x=”0″ y=”190″ width=”180″ height=”38″/>
<mx:Button id=”t_btn_Send” x=”128″ y=”235″ label=”发送” enabled=”false” click=”SendMessage()”/>
</mx:Canvas>
</mx:Application>