前段时间听说flashplayer已经开始支持p2p了,对这块非常感兴趣于是开始玩flex,一玩下来不可自拔。用Stratus搭建p2p环境如此简单,双方只需要能连上Stratus服务器就能直接进行语音视频的聊天,不需要任何客户端。Adobe还真是NB,呵呵出了这么XX的东东,前途无可限量啊。

闲话少说,这几天小搞了下,用Mysql和flex弄了个简易随机视频聊天室,和有共同爱好的各位一起分享下。高手可以直接飞过哈,欢迎提出意见,毕竟刚玩没多久,问题很多。

准备工作:

1.我用的是Flash Builder 4开发的,默认用的是Flex SDK4.0

2.开发最好用flash payer 10 debug version(可以进行调试)

3.申请一个 Stratus develper key:8b0f114ef5a20c433d5c2a33-201aeea5601b(用这个也行-_-我申请的)或者在这申请

 下面正式开始,程序主体主要分为3大块:

一、p2p语音视频功能模块

 这个模块网上有很多教程了,Adobe官方的那个Sample就很好。我就是以此为基础进行开发的。可能有些朋友还不是很了解,为了每个人都能搞清楚,下面针对代码详细地进行下介绍。

由于是p2p模式,每个用户既是呼叫者又是被呼叫者。更具体点,举个例子有两个人A和B打电话。A呼叫B,此时A是呼叫者,B是被呼叫者。反之B是呼叫者,A是被呼叫者。因此在每个p2p模块中必须要有呼叫者和被呼叫者两个部分。

首先要连接上stratus服务器:

 

private var netConnection:NetConnection;
//监听信息流
private var listenStream:NetStream;    
//播放上面的监听流
private var controlStream:NetStream;  
//对外发布信息流
private var outgoingStream:NetStream; 
//接受外部发送的信息流 
private var incomingStream:NetStream;
//Adobe stratus 服务器地址
private const SERVER:String="rtmfp://stratus.adobe.com/"; 
//Developer Key,要自己申请
private const DEVKEY:String="8b0f114ef5a20c433d5c2a33-201aeea5601b";
//创建一个nectConnetion与stratus 服务器进行连接
netConnection=new NetConnection();
netConnection.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
//连接Adobe stratus 服务器
netConnection.connect(SERVER+DEVKEY);

被呼叫部分:

                  //被呼叫者发布监听流,以便呼叫者连接
                         listenStream = new NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
			  listenStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);	
			  listenStream.publish(username);	
		//呼叫者一旦订阅上面的监听流,就会触发onPeerConnect事件	
			  var c:Object =new Object();
			  c.onPeerConnect = function(caller:NetStream):Boolean
			  {
				if(callState == CallReady)  
				{

                          //接受呼叫者发布的视频语音流
					callState = CallRinging;
					idManager.change(callState);
					busyState = busyOn;
					incomingStream = new NetStream(netConnection,caller.farID);
					incomingStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
					video =new Video();
					video.attachNetStream(incomingStream);
					remoteVideoDisplay.addChild(video);
					incomingStream.play("caller");
					
					var st:SoundTransform = new SoundTransform(speakerVolumeSlider.value);
					incomingStream.soundTransform = st;
		//处理呼叫的方法
					var i:Object = new Object();
					i.onIncomingCall = function(caller:String):void
					{
						if(callState != CallRinging)
						{
							txtInfo.text += "onIncomingCall: Wrong call state: " + callState + "\n";
						    return;
						}
						send_bn.enabled=true;
						txtInfo.text +=  caller + "已经成功与您连接上\n";
						partnername = caller;
						
						//outgoingStream.send("onCallAccepted", username);
						callState = CallEstablished;
						//idManager.change(callState);
					}	
				     i.onIm = function(caller:String,text:String):void
					 {
						 txtMessage.text += caller+": "+text+"\n";
					 }
				     i.onDisconnected = function(caller:String):void
					 {
						 txtInfo.text += caller+"和你断开连接\n";
						 send_bn.enabled=false;
						 stop();
					 }	 
					incomingStream.client = i;
		//对呼叫者发布自己的语音视频流	
					outgoingStream = new NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
					outgoingStream.addEventListener(NetStatusEvent.NET_STATUS,callee_outgoingStreamHandler);
					outgoingStream.attachCamera(camera);
					outgoingStream.attachAudio(mic);
					outgoingStream.publish("callee");
					
					return true;
				   }
				txtInfo.text += "onPeerConnect: all rejected due to state: " + callState + "\n";
				
				return false;
			  }  
				listenStream.client = c;
				

				
				callState = CallReady;		

呼叫部分:

                                        callState = CallCalling;
			  idManager.change(callState);
			  busyState = busyOn;
                                         //播放被呼叫者的监听流
			  controlStream = new NetStream(netConnection,farPeerID);
			  controlStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
			  controlStream.play(partnername);
			  //对被呼叫者发布自己的视频语音流
			  outgoingStream = new NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
			  outgoingStream.addEventListener(NetStatusEvent.NET_STATUS,caller_outgoingStreamHandler);
			  outgoingStream.attachCamera(camera);
			  outgoingStream.attachAudio(mic);
			  outgoingStream.publish("caller");
			  txtInfo.text += "正在与"+partnername+"建立连接\n";
                                         //播放被呼叫者的视频语音流
			  	  incomingStream = new NetStream(netConnection,farPeerID);
				  incomingStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
				  video =new Video();
				  video.attachNetStream(incomingStream);
				  remoteVideoDisplay.addChild(video);
				  incomingStream.play("callee");
				  
				  var st:SoundTransform = new SoundTransform(speakerVolumeSlider.value);
				  incomingStream.soundTransform = st;
		             //处理呼叫的方法
				  var i:Object = new Object();
				  i.onIm = function(callee:String,text:String):void
				  {
					  txtMessage.text += callee+": "+text+"\n";
				  }
				  i.onCallAccepted = function(callee:String):void
				  {
					  if (callState != CallCalling)
					  {
						  txtInfo.text += "连接失败";
						  return;
					  }
					  send_bn.enabled=true;
					  txtInfo.text += callee+"已经成功与您连接上\n";
					  callState = CallEstablished;
					  //idManager.change(callState);
				  }
				  i.onDisconnected = function(callee:String):void
				  {
					  txtInfo.text += callee+"和你断开连接\n";
					  send_bn.enabled=false;
					  stop();
				  }
				  incomingStream.client = i;

二、用户数据库及webservice接口

   这个项目我使用MySQL进行用户数据库设计,用户端产生相应行为后(如登录,下线,聊天,空闲等)会在数据库中更新自己的状态,其他用户根据查询得到的结果进行操作。数据库结构见下图:

                                                                 用户名           peerID        更新时间              在线状态    聊天状态

   该项目只要进行简单的数据库操作就可以了,所以我使用了HTTPService组件和一个简单的php网页进行与数据库的交互。

   HTTPService组件位于mx.rpc.http包下,当调用 HTTPService 对象的 send() 方法时,将发出对指定 URL 的 HTTP 请求,并且返回 HTTP 响应。可以选择向指定 URL 传递参数。项目中通过HTTPService组件传递用户端的参数到php网页,由php根据相应的参数操作数据库,返回相应的结果交由用户端执行。

   HTTPService组件部分:

         mHttpService = new HTTPService();
            mHttpService.url = mWebServiceUrl;
            //添加对结果事件的侦听
            mHttpService.addEventListener("result", httpResult);
            mHttpService.addEventListener("fault", httpFault);
             //实例化request请求对象并发送  
            var request:Object = new Object();
	var now:Date = new Date();
	request.time = now.getTime();
            request.username = user;
            request.identity = id;
            request.online = x;
            request.busy = x;
            mHttpService.cancel();
            mHttpService.send(request);

  php网页部分:    

<?php
$output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
$output .= "<result>";
if ( isset($_GET["username"]) || isset($_GET["friends"]) || isset($_GET["fetch"]))
{
	define( "DATABASE_SERVER", "localhost" );
	define( "DATABASE_USERNAME", "root" );
	define( "DATABASE_PASSWORD", "root");
	define( "DATABASE_NAME", "root" );
	$mysql = mysql_connect( DATABASE_SERVER, 
			         DATABASE_USERNAME, 
			         DATABASE_PASSWORD ) 
			or die( mysql_error() );
	mysql_select_db( DATABASE_NAME );	
}
//注册更新部分
if ( isset($_GET[ "username" ]) )
{
	
	$user 	= mysql_real_escape_string( $_GET[ "username" ] );
	$identity	= mysql_real_escape_string( $_GET[ "identity" ] );
	$online = mysql_real_escape_string( $_GET[ "online" ] );
	$busy = mysql_real_escape_string( $_GET[ "busy" ] );

	$query = "SELECT * FROM registrations WHERE m_username='$user' ;";
	$result = mysql_query( $query );
	
	$isNew = ($result && mysql_num_rows($result) == 0);
	if( $isNew ) {
		$query = "INSERT INTO registrations SET ";
		$query .= "m_username='$user', m_identity='$identity', m_updatetime=now(), m_online='$online', m_busy='$busy';";
	} else {
		$query = "UPDATE registrations SET ";
		$query .= " m_identity='$identity', m_updatetime=now(), m_online='$online', m_busy='$busy' where m_username='$user';";
	}
     $result = mysql_query( $query );
	if( $isNew )
		$output .= "<update>true</update>";
	else
		$output .= "<update>false</update>";
}
//查找用户部分
if ( isset($_GET[ "friends" ]) )
{
	$friends	= mysql_real_escape_string( $_GET[ "friends" ] );
	$output .= "<friend><user>$friends</user>";
	$query = "select * from registrations where m_username = '$friends' and TIMEDIFF(now(),m_updatetime) >0 ;";
	
	$result = mysql_query( $query );
	if( $result ) {
		while( $item = mysql_fetch_assoc( $result ) ) {
			    $output .= "<identity>".$item["m_identity"]."</identity>";
		}
	}
	$output .= "</friend>";
}
//获取返回结果
if ( isset( $_GET["fetch"]) )
{
	$time = mysql_real_escape_string( $_GET[ "time" ] );
	$query = "select m_number from registrations order by m_username asc ;" ;
	
	$result =mysql_query($query);
	if( $result ) 
	{
	$num = mysql_num_rows($result);
	$output .= "<fetch><num>".$num."</num>";
	}
	
	$query = "select m_username from registrations where m_online ='1' and m_busy = '0' order by m_username asc ;";
	$result = mysql_query($query);
	
	if( $result )  
	{
		$onlinenum = mysql_num_rows($result);
		$output .="<onlinenum>".$onlinenum."</onlinenum><users>";
		$i = 1;
		while ($row = mysql_fetch_row($result))
		{
			$output .= "<user".$i.">".$row[0]."</user".$i.">";
			$i++;
		}
			$output .= "</users></fetch>";"; 
	}
}

	


$output .= "</result>";

header( "Content-Type: text/xml" ); 
echo $output;
?>

 

 

 数据库返回所有用户结果如下:

 

代码
<?xml version="1.0" encoding="utf-8" ?>
-
<result>
-
<fetch>
<num>109</num>
<onlinenum>10</onlinenum>
-
<users>
<user1>a</user1>
<user2>左江</user2>
<user3>eee</user3>
<user4>jacksonchina</user4>
<user5>king</user5>
<user6>lll</user6>
<user7>rinima</user7>
<user8>sss</user8>
<user9>ssssssssssss</user9>
<user10>tst</user10>
</users>
<chaternum>20</chaternum>
-
<chaters>
<chater1>11</chater1>
<chater2>1234</chater2>
<chater3>3323</chater3>
<chater4>7</chater4>
<chater5>aaaaabb</chater5>
<chater6>双</chater6>
<chater7>bearsea</chater7>
<chater8>cccc</chater8>
<chater9>ccvv</chater9>
<chater10>ee</chater10>
<chater11>hulu</chater11>
<chater12>jh</chater12>
<chater13>joe</chater13>
<chater14>perry</chater14>
<chater15>sg</chater15>
<chater16>v</chater16>
<chater17>wx</chater17>
<chater18>xx</chater18>
<chater19>xzr</chater19>
<chater20>所得税的</chater20>
</chaters>
<time />
</fetch>
</result>

 

 

三、主程序部分

             主程序界面实时显示在线用户状态,可以双击列表空闲用户进行聊天也可以点击随机按钮随机选取在线空闲用户进行聊天。

             退出时记得点击离开正常退出哈。

             本程序实现非常简单,只需要一个Mysql数据库即可,缺陷是如果用户异常退出,状态信息不能即时在数据库中更新,会出现问题(如用户直接关闭程序进程,实际应该下线,但未更新数据库,所以状态还显示为在线)。仅供测试玩

            Demo:http://flexmysql.online.cm/p2pChatRoom.swf  

             有问题欢迎交流-_-    转载请注明出处 by疯狂的八神庵

             flex 完整代码在下面:

             

import flash.external.ExternalInterface;
			
			import mx.events.FlexEvent;
			import mx.events.ListEvent;
			import mx.events.SliderEvent;
			import mx.managers.PopUpManager;
			
			import spark.components.TitleWindow;
			
			private var netConnection:NetConnection;
			private var netStream:NetStream;
			private var publishStream:NetStream;
			private var receiveStream:NetStream;
			private var mic:Microphone;
			private var camera:Camera;
			private var video:Video;
			private var windowisopen:Boolean = false;
			
			private var listenStream:NetStream;	
			private var controlStream:NetStream;
			private var outgoingStream:NetStream;
			private var incomingStream:NetStream;
			
			
			// login/registration state machine
            [Bindable] private var loginState:int;
			
			private const LoginNotConnected:int = 0;
			private const LoginConnected:int = 1;
			// call state machine
			
			[Bindable] private var busyState:int;
			private const busyOn:int = 1;
			private const busyOff:int = 0;
			

			private var callState:int;
			
			private const CallNotReady:int = 0;
			private const CallReady:int = 1;
			private const CallCalling:int = 2;
			private const CallRinging:int = 3;
			private const CallEstablished:int = 4;
			private const CallFailed:int = 5;

			[Bindable] private var username:String;
			[Bindable] private var partnername:String;
			private var partner:String;
			private var login:int; 
			
			

			[Bindable] private var userArray:Array;
			[Bindable] private var chaterArray:Array;

			private var farPeerID:String;
			
			private var idManager:AbstractIdManager;
			
			[Bindable] private var number:int;
			[Bindable] private var onlinenum:int;
			[Bindable] private var chaternum:int;
			
			[Bindable] private var micNames:Array;
			private var micIndex:int=0;
			
			[Bindable] private var cameraNames:Array;
			private var cameraIndex:int=0;
			//[Bindable] private var neighborCount:int=0;
			//[Bindable] private var vacantCount:int=0;
			
			private const SERVER:String="rtmfp://stratus.adobe.com/";
			private const DEVKEY:String="8b0f114ef5a20c433d5c2a33-201aeea5601b";
			private const GROUP_PREFIX:String = "com.p2p.chatroom";
			private const WebServiceUrl:String = "http://p2pchat.online.cm/reg.php";
						
			/*private static var FUNCTION_USEREXIT:String =    
				"document.insertScript = function () " +   
				"{ " +   
				"window.onbeforeunload = function() " +   
				"{ " +   
				"var flexObj = p2pChatRoom.checkExit(); " +//MyTest2是swf在html中object的id   
				"if(flexObj != \"\") " +   
				"{ " +   
				"return flexObj; " +   
				"}else{ " +   
				"return; " +   
				"} " +   
				"} " +   
				"} ";  
			*/
			
			private static var FUNCTION_USEREXIT:String =    
				"document.insertScript = function () " +   
				"{ " +   
				"window.onbeforeunload = function() " +   
				"{ " +   
				"var flexObj = p2pChatRoom.checkExit(); " +//MyTest2是swf在html中object的id   
				"if(flexObj != \"\") " +   
				"{ " +   
				"return flexObj; " +   
				"}else{ " +   
				"return; " +   
				"} " +   
				"} " +   
				"} ";  
			
			private function innit():void
			{
				//disconnect_btn.enabled=false;
				clear_btn.enabled=true;
				send_bn.enabled=false;
				random_btn.enabled = false;
				loginState = LoginNotConnected;
				busyState = busyOff;
				txtInfo.text+="------------------------------------------------------偶素华丽的分割线------------------------------------------------------\n欢迎加入p2p聊天世界,右边列表显示所有当前在线的用户。你可以直接鼠标双击目标用户直接进行视频语音聊天!退出时请点击离开防止信息丢失,玩的愉快\n";
				if(!windowisopen)
				{
					openLoginWindow();
				}
					
				   
				ExternalInterface.call(FUNCTION_USEREXIT);   
				ExternalInterface.addCallback("checkExit",checkExit);  
				
			}
			
			private function checkExit():String {   
				doExit(); 
				var userExitStr:String = "欢迎您下次光临!";   
				return userExitStr;
			}  
			
			private function doExit():void
			{
				
				
				if (callState == CallEstablished && outgoingStream)
				{	
					outgoingStream.send("onDisconnected",username);
					txtInfo.text += "和"+partnername+"断开连接\n";
					partnername = null;
				}
				if (idManager)
				{
					idManager.unregister();
				}
				
				if(netConnection)
				{
					netConnection.close();
					txtInfo.text+="正在与服务器断开连接\n";
				}
				
				innit();
			}
			private function openLoginWindow():void
			{
				var loginWindow:myLoginWindow=myLoginWindow(PopUpManager.createPopUp(this,myLoginWindow,true));
				loginWindow.txtInput = txtID ;
				PopUpManager.centerPopUp(loginWindow);  
				loginWindow.addEventListener(MyEvent.Login_OK,connect_btn_clickHandler);
				windowisopen = true;
			}
			protected function connect_btn_clickHandler(e:MyEvent):void
			{
				windowisopen = false;
				username=txtID.text;
				txtMessage.text="";
				//userList.doubleClickEnabled=true;
				innitApp();
				
				netConnection=new NetConnection();
				netConnection.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
				netConnection.connect(SERVER+DEVKEY);
			}
			private function innitApp():void
			{
				var mics:Array=Microphone.names;
				if(mics)
				{
					micNames=mics;
				}
				else
				{
					txtMessage.text+="没有检测到麦克风\n";
				}
				
				var cams:Array=Camera.names;
				if(cams)
				{
					cameraNames=cams;
				}
				else
				{
					txtMessage.text+="没有检测到摄像头\n";
				}
				
				micSelection.selectedIndex=micIndex;
				cameraSelection.selectedIndex=cameraIndex;
				
				mic=Microphone.getMicrophone(micIndex);
				camera=Camera.getCamera(cameraIndex.toString());
				
				var speakerVolume:Number = 0.8;
				speakerVolumeSlider.value = speakerVolume;
				
				var micVolume:int = 20;
				micVolumeSlider.value = micVolume;
				
				if (mic)
				{
					mic.codec=SoundCodec.SPEEX;
					mic.encodeQuality=3;
					mic.setSilenceLevel(10);
					mic.setUseEchoSuppression(true);
					mic.gain = micVolume;
					
					//mic.addEventListener(StatusEvent.STATUS, onDeviceStatus);
					//mic.addEventListener(ActivityEvent.ACTIVITY, onDeviceStatus);
					
				}
				else
				{
					txtMessage.text+="启动本地麦克风失败\n";
				}
				
				if (camera)
				{
					//camera.addEventListener(StatusEvent.STATUS, onDeviceStatus);
					//camera.addEventListener(ActivityEvent.ACTIVITY, onDeviceStatus);
					
					camera.setMode(320, 240, 10);
					camera.setQuality(360000 / 8, 0);
					camera.setKeyFrameInterval(15);
					
					myVideoDisplay.attachCamera(camera);
					
					
				}
				else
				{
					txtMessage.text+="启动本地摄像头失败\n";
				}
			}
			private function onDeviceStatus(e:StatusEvent):void
			{
				
			}
				
			
            private function initStream():void
			{
			  listenStream = new NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
			  listenStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);	
			  listenStream.publish(username);	
				
			  var c:Object =new Object();
			  c.onPeerConnect = function(caller:NetStream):Boolean
			  {
				if(callState == CallReady)  
				{

					callState = CallRinging;
					idManager.change(callState);
					busyState = busyOn;
					incomingStream = new NetStream(netConnection,caller.farID);
					incomingStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
					video =new Video();
					video.attachNetStream(incomingStream);
					remoteVideoDisplay.addChild(video);
					incomingStream.play("caller");
					
					var st:SoundTransform = new SoundTransform(speakerVolumeSlider.value);
					incomingStream.soundTransform = st;
					
					var i:Object = new Object();
					i.onIncomingCall = function(caller:String):void
					{
						if(callState != CallRinging)
						{
							txtInfo.text += "onIncomingCall: Wrong call state: " + callState + "\n";
						    return;
						}
						send_bn.enabled=true;
						txtInfo.text +=  caller + "已经成功与您连接上\n";
						partnername = caller;
						
						//outgoingStream.send("onCallAccepted", username);
						callState = CallEstablished;
						//idManager.change(callState);
					}	
				     i.onIm = function(caller:String,text:String):void
					 {
						 txtMessage.text += caller+": "+text+"\n";
					 }
				     i.onDisconnected = function(caller:String):void
					 {
						 txtInfo.text += caller+"和你断开连接\n";
						 send_bn.enabled=false;
						 stop();
					 }	 
					incomingStream.client = i;
					
					outgoingStream = new NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
					outgoingStream.addEventListener(NetStatusEvent.NET_STATUS,callee_outgoingStreamHandler);
					outgoingStream.attachCamera(camera);
					outgoingStream.attachAudio(mic);
					outgoingStream.publish("callee");
					
					return true;
				   }
				txtInfo.text += "onPeerConnect: all rejected due to state: " + callState + "\n";
				
				return false;
			  }  
				listenStream.client = c;
				

				
				callState = CallReady;		
			}
			
			
			private function call():void
			{
				
			  callState = CallCalling;
			  idManager.change(callState);
			  busyState = busyOn;
			  controlStream = new NetStream(netConnection,farPeerID);
			  controlStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
			  controlStream.play(partnername);
			  
			  outgoingStream = new NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
			  outgoingStream.addEventListener(NetStatusEvent.NET_STATUS,caller_outgoingStreamHandler);
			  outgoingStream.attachCamera(camera);
			  outgoingStream.attachAudio(mic);
			  outgoingStream.publish("caller");
			  //outgoingStream.send("onIncomingCall",username);
			  txtInfo.text += "正在与"+partnername+"建立连接\n";
			  
			  //var o:Object = new Object();
			  //o.onPeerConnect = function(callee:NetStream):Boolean
			  //{
				  incomingStream = new NetStream(netConnection,farPeerID);
				  incomingStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
				  video =new Video();
				  video.attachNetStream(incomingStream);
				  remoteVideoDisplay.addChild(video);
				  incomingStream.play("callee");
				  
				  var st:SoundTransform = new SoundTransform(speakerVolumeSlider.value);
				  incomingStream.soundTransform = st;
				  
				  var i:Object = new Object();
				  i.onIm = function(callee:String,text:String):void
				  {
					  txtMessage.text += callee+": "+text+"\n";
				  }
				  i.onCallAccepted = function(callee:String):void
				  {
					  if (callState != CallCalling)
					  {
						  txtInfo.text += "连接失败";
						  return;
					  }
					  send_bn.enabled=true;
					  txtInfo.text += callee+"已经成功与您连接上\n";
					  callState = CallEstablished;
					  //idManager.change(callState);
				  }
				  i.onDisconnected = function(callee:String):void
				  {
					  txtInfo.text += callee+"和你断开连接\n";
					  send_bn.enabled=false;
					  stop();
				  }
				  incomingStream.client = i;
				 // return true;
			 // }
			 // outgoingStream.client = o;
			  

			  
			}
			
			
			

			
			private function sendSomeData():void{
				if(txtSend.text != "")
				{
				txtMessage.text += username + ":" + txtSend.text + "\n";
				outgoingStream.send("onIm",username,txtSend.text);
				txtSend.text = " ";
				}
				else
					txtMessage.text += "-_-请输入内容\n";
			}
			
            private function caller_outgoingStreamHandler(e:NetStatusEvent):void
			{
				if (e.info.code == "NetStream.Play.Start" )
				{
					outgoingStream.send("onIncomingCall",username);
				}
			}
			
			private function callee_outgoingStreamHandler(e:NetStatusEvent):void
			{
				if (e.info.code == "NetStream.Play.Start" )
				{
					outgoingStream.send("onCallAccepted",username);
				}
			}
			
            private function statusHandler(event:NetStatusEvent):void
			{
				trace(event.info.code);
				switch(event.info.code)
				{
					case"NetConnection.Connect.Success":
						onConnect();
						break;
					case"NetConnection.Connect.Closed":
						onDisconnect();
						break;
					case "NetConnection.Connect.Failed":
					case "NetConnection.Connect.Rejected":
						txtInfo.text+="连接到服务器失败\n";
						loginState = LoginNotConnected;
						break;
					//case "NetStream.Play.Start":
						//onPlay();
						//break;
					//case "NetStream.Connect.Success":
						//break;
					case "NetStream.Connect.Rejected":
					case "NetStream.Connect.Failed":
						txtInfo.text+="呼叫失败\n";
						break;					
						
				}
			}
			
			private function onConnect():void
			{
				    userList.doubleClickEnabled=true;
					chatList.doubleClickEnabled=true;
				    login=1;	
					callState = CallNotReady;

					txtInfo.text+=username+"与服务器连接成功\n";
					
					idManager = new HttpIdManager();
					idManager.addEventListener("registerSuccess", idManagerEvent);
					idManager.addEventListener("registerFailure", idManagerEvent);
					idManager.addEventListener("lookupFailure", idManagerEvent);
					idManager.addEventListener("lookupSuccess", idManagerEvent);
					idManager.addEventListener("idManagerError", idManagerEvent);
					idManager.addEventListener("fetchSuccess", idManagerEvent);
					idManager.addEventListener("fetchFailure", idManagerEvent);
					idManager.addEventListener("unregisterFailure", idManagerEvent);
					
					idManager.service = WebServiceUrl;
					idManager.register(username, netConnection.nearID);
			
					userList.addEventListener(ListEvent.ITEM_DOUBLE_CLICK,p2pHandler);
					chatList.addEventListener(ListEvent.ITEM_DOUBLE_CLICK,ppHandler);
			}
			
			private function onDisconnect():void
			{
				login=0;

				send_bn.enabled=false;
				txtInfo.text+= "与服务器断开连接\n";
				chatList.dataProvider = null;
				userList.dataProvider = null;
				partnername = null;
				number=0;
				onlinenum=0;
				idManager = null;
				callState = CallNotReady;
				
				if(video)
				{
					remoteVideoDisplay.removeChild(video);
				}
				
				if (incomingStream)
				{
					incomingStream.close();
					incomingStream.removeEventListener(NetStatusEvent.NET_STATUS, statusHandler);
				}
				
				if (outgoingStream)
				{
					outgoingStream.close();
					outgoingStream.removeEventListener(NetStatusEvent.NET_STATUS, statusHandler);
				}
				
				if (controlStream)
				{
					controlStream.close();
					controlStream.removeEventListener(NetStatusEvent.NET_STATUS, statusHandler);
				}
				
				
				incomingStream = null;
				outgoingStream = null;
				controlStream = null;
				netConnection = null;
				innit();
				
			}
			
			private function idManagerEvent(e:Event):void
			{
				//status("ID event: " + e.type + "\n");
				trace(e.type);
				switch(e.type)	
			  {	
				case "registerSuccess":
				case "registerFailure":		
					
					if(login == 1)
					{
						txtInfo.text+="恭喜"+username+"登陆成功\n";
						idManager.fetch();	
						initStream();
						login++;
						loginState = LoginConnected;
						random_btn.enabled = true;
					}
					else if(login == 0)
					{
						return;
					}
					else
					{
						login++;
						trace("更新第"+login+"次");
						idManager.fetch();
					}
					break;
				case  "lookupSuccess":
					// party query response
					var i:IdManagerEvent = e as IdManagerEvent;			
					partnername=i.user;
					farPeerID=i.id;
					call();
					break;
				case "fetchSuccess":				
					var j:FetchDataEvent = e as FetchDataEvent;
					onlinenum = j.onlinenum;
					number = j.num;
					userArray = j.arr;
					
					chaternum = j.chaternum;
					chaterArray = j.chatarr;

                    break;
				default:
					// all error messages ar IdManagerError type
					/*
						var error:IdManagerError = e as IdManagerError;
					trace("Error description: " + error.description + "\n")
					*/
					login=0;
					txtInfo.text+="查询失败,发生错误\n";
					/*
					if (callState == CallEstablished && outgoingStream)
					{	
						outgoingStream.send("onDisconnected",username);
						txtInfo.text += "和"+partnername+"断开连接\n";
						partnername = null;
					}
					if (idManager)
					{
						idManager.unregister();
					}
					
					if(netConnection)
					{
						netConnection.close();
						
					};
					*/
					break;
				}
			}
			
			private function p2pHandler(evt:ListEvent):void
			{

				if((evt.target.selectedItem.label != username) && callState == 1)
				{
				 idManager.lookup(evt.target.selectedItem.label);
				}
				if(evt.target.selectedItem.label == username)
				{	
				 txtMessage.text += "-_- 你不能与自己聊天\n";
				}
				if((evt.target.selectedItem.label != username) && (callState == 2 || callState == 3 || callState == 4))
				{
					txtMessage.text += "-_- 聊天中无法和其他人聊天 ,请先停止\n";
				}
			}
			
			private function ppHandler(evt:ListEvent):void
			{
				if(evt.target.selectedItem.label == username)
				{	
					txtMessage.text += "-_- 你不能与自己聊天\n";
				}
				else
				{
					txtMessage.text += "-_- 对方正在聊天中 无法打断\n";
				}
			}
			
			protected function clear_btn_clickHandler(event:MouseEvent):void
			{
				txtMessage.text="";
				txtInfo.text = "";
			}


			protected function disconnect_btn_clickHandler(event:MouseEvent):void
			{
				txtInfo.text+="正在与服务器断开连接\n";
				
				if (callState == CallEstablished && outgoingStream)
				{	
					outgoingStream.send("onDisconnected",username);
					txtInfo.text += "和"+partnername+"断开连接\n";
					partnername = null;
				}
				if (idManager)
				{
					idManager.unregister();
				}
				
				if(netConnection)
				{
					netConnection.close();
					
				}
			}


			protected function stop_btn_clickHandler(event:MouseEvent):void
			{
				if (callState == CallEstablished && outgoingStream)
				{	
					outgoingStream.send("onDisconnected",username);
					txtInfo.text += "和"+partnername+"断开连接\n";
					stop();
				}
			}
			
			private function stop():void
			{
				partnername = null;
				callState = CallReady;
				busyState = busyOff;
				if(video)
				{
					remoteVideoDisplay.removeChild(video);
					video = null;
				}
				if (incomingStream)
				{
					incomingStream.close();
					incomingStream.removeEventListener(NetStatusEvent.NET_STATUS, statusHandler);
				}
				
				if (outgoingStream)
				{
					outgoingStream.close();
					outgoingStream.removeEventListener(NetStatusEvent.NET_STATUS, statusHandler);
				}
				
				if (controlStream)
				{
					controlStream.close();
					controlStream.removeEventListener(NetStatusEvent.NET_STATUS, statusHandler);
				}
				idManager.change(callState);
			}


			protected function random_btn_clickHandler(event:MouseEvent):void
			{
				txtMessage.text += "正在查询空闲用户\n";
				if(callState != CallReady)
				{
					txtMessage.text += "请先停止聊天\n";
				}
				else
				{
					var i:int = int(Math.random()*userArray.length);
					
					if(userArray[i].label == username && userArray.length == 1)
					{
						txtMessage.text += "无空闲用户\n";
					}
					else if(userArray[i].label == username)
					{
						do
						{
							var i:int = int(Math.random()*userArray.length);
						}
						while(userArray[i].label == username);
						
						txtMessage.text += "查找成功\n";
						idManager.lookup(userArray[i].label);
					}
					else 
					{
						txtMessage.text += "查找成功\n";
						idManager.lookup(userArray[i].label);
					}
				}
			}

protected function cameraChanged():void
			{
				cameraIndex=cameraSelection.selectedIndex;
				camera=Camera.getCamera(cameraIndex.toString());
				if (camera)
				{
					camera.setMode(320, 240, 10);
					camera.setQuality(360000 / 8, 0);
					camera.setKeyFrameInterval(15);
					
					myVideoDisplay.attachCamera(camera);
					
					if (callState == CallEstablished)
					{	
						outgoingStream.attachCamera(camera);
					}
					
					
				}
				else
				{
					txtMessage.text+="启动本地摄像头失败";
				}
			}
			
			private function micChanged():void
			{
				micIndex = micSelection.selectedIndex;
				
				mic = Microphone.getMicrophone(micIndex);
				
				if (mic)
				{
					mic.codec=SoundCodec.SPEEX;
					mic.encodeQuality=3;
					mic.setSilenceLevel(5);
					mic.setUseEchoSuppression(true);
					
					if (callState == CallEstablished)
					{	
						outgoingStream.attachAudio(mic);
					}
				}
				else
				{
					txtMessage.text+="启动本地麦克风失败";
				}
			}


			private function micVolumeChanged(e:SliderEvent):void
			{
				var mic:Microphone = Microphone.getMicrophone(micIndex);
				if (mic)
				{
					mic.gain = e.value;
				}
			}
			
			private function speakerVolumeChanged(e:SliderEvent):void
			{
				if (incomingStream)
				{
					var st:SoundTransform = new SoundTransform(e.value);
					incomingStream.soundTransform = st;
					
				}
				
			}