即然不小心標題多打了視訊2個字
現在要改也來不及惹QQ
但視訊網站的模式有很多種
我想 這樣好了
我們來做這個~
點擊房間列表裡的使用者 然後可以選 觀看對方視訊
而LAYOUT也要改一下
如果是觀看對方視訊的同時,聊天介面就會切成上下2半
上面是視訊
視訊再分成2半,左邊是對方,右邊是自已
然後要看對方視訊時,會有邀請的要求給對方
對方同意後 才會打開 視訊的介面出來
這時候第三方要邀請已經在視訊中的人,都會回傳 對方已在視訊聊天中
嗯嗯 就這樣~那就開始ㄅ
通常server code會比較容易看懂,也比較容易了解整個的流程
延展教學(2)的範例,新增了幾個主要的method
client.invite = function(id)//發起邀請
client.sendInviteResponse = function(id,isAccept)//接收方回應是否接受邀請
application.onPublish = function(client, myStream)//發佈視訊
client.exitPrivate = function()//通知要離開private視訊
exitPrivate(user)//離開private視訊
這裡比較重要的地方是,要發佈一個視訊,需要有一個stream name
可以自已定一個字串,但不可以重覆。
假如a發佈一個video_a的串流,那麼 其他使用者只要去聽這個串流名稱video_a就可以看到a所發佈的視訊
client的程式裡有一個特別的用法,你可以在限定參數的event function內,帶入各種想要的參數
Alert.show(userName+"邀請你進入視訊聊天,是否接受?", "hasInvite", Alert.OK|Alert.NO,null,addInviteArguments(onHasInvite,id));
private function addInviteArguments(method:Function, id:String):Function
{
return function(event:Event):void {method.apply(null, [event].concat(id));}
}
private function onHasInvite(e:CloseEvent,id:String):void{
mineNc.call("sendInviteResponse",null,id,Boolean(e.detail==Alert.OK));
}
常常會遇到這種onEvent的參數,不能多帶參數的情況,這時候使用這個方式,可以任意帶多個參數進去;就不用另外做一個變數來記著了。
其它部份就直接看code吧~
server code:
var roomUser = new Array();
var ERROR_REPEAT_LOGIN=0;
var USER_STATUS_NORMAL=0;
var USER_STATUS_PRIVATE=1;
var INVITE_RESULT_PRIVATE=0;
var INVITE_RESULT_SUCCESS=1;
var INVITE_RESULT_OFF_LINE=2;
var INVITE_RESULT_REJECT=3;
application.onAppStart = function (){
}
// Called when a client connects
application.onConnect = function(client,userName) {
var _user=getUserByUserName(userName);
if(_user)
{
application.rejectConnection(client,ERROR_REPEAT_LOGIN);
return;
}
var user=new Object();
user.userName=userName;
user.id=client.id;
user.client=client;
user.status=USER_STATUS_NORMAL;
user.streamId="";
user.targetUserId=null;
roomLogin(user);
client.chatToAll = function(msg) {
var user=getUserByClient(client);
application.broadcastMsg("chatReceive",user.userName,msg);
}
//發起邀請
client.invite = function(id) {
var user=getUserByClient(client);
var targetUser=getUserById(id);
if(targetUser)
{
if(targetUser.status==USER_STATUS_PRIVATE)
client.call("onInvite",null,INVITE_RESULT_PRIVATE);//回應 對方已在視訊中
else
{
client.call("onInvite",null,INVITE_RESULT_SUCCESS);//回應 等待對方接受
targetUser.client.call("hasInvite",null,user.id,user.userName);//通知 對方 有人邀請你
}
}
else
client.call("onInvite",null,INVITE_RESULT_OFF_LINE);//回應 對方已不在線上
}
//接收方回應是否接受邀請
client.sendInviteResponse = function(id,isAccept) {
var user=getUserByClient(client);
var targetUser=getUserById(id);
if(isAccept)//如果同意 就把雙方各建立一個stream的id,並讓雙方得知自已跟對方的stream id
{
var r=Math.floor(Math.random()*1000);
user.streamId=r+user.id;
targetUser.streamId=r+targetUser.id;
//事實上這裡可以做個更好的加密 比如說拿雙方的id再加上一個變數來md5一下
user.targetUserId=targetUser.id;
targetUser.targetUserId=user.id;
//互相記住對方的id,若斷線的時後可以通知對方
client.call("intoPrivate",null,user.streamId,targetUser.streamId);
targetUser.client.call("intoPrivate",null,targetUser.streamId,user.streamId);
//進入雙人視訊模式 參數1是自已的stream id,參數2是對方的stream id
user.status=USER_STATUS_PRIVATE;
targetUser.status=USER_STATUS_PRIVATE;
}
else
targetUser.client.call("onInvite",null,INVITE_RESULT_REJECT);
}
client.exitPrivate = function() {
var user=getUserByClient(client);
exitPrivate(user);
}
}
// Called when the client publishes
application.onPublish = function(client, myStream) {
nc = new NetConnection();
ns = new NetStream(nc);
ns.setBufferTime(2);
ns.attach(myStream);
ns.publish( myStream.name, "live" );
}
application.onUnpublish = function( client, myStream ) {
var user=getUserByClient(client);
application.broadcastMsg("systemMessage",user.userName+":"+myStream.name);
trace(myStream.name + " is unpublishing" );
}
// Called when a client disconnects
application.onDisconnect = function(client) {
var user=getUserByClient(client);
exitPrivate(user);
removeUser(user);
}
function exitPrivate(user)
{
user.status=USER_STATUS_NORMAL;
user.targetUserId=null;
var targetUser=getUserById(user.targetUserId);
if(targetUser)
{
targetUser.status=USER_STATUS_NORMAL;
targetUser.targetUserId=null;
targetUser.client.call("onExitPrivate",null);
}
}
function roomLogin(user)
{
application.acceptConnection(user.client);
application.broadcastMsg("systemMessage", user.userName+" in room");
roomUser.push(user);
application.broadcastMsg("userList",roomUser);
user.client.call("loginSuccess",null,user);
}
function removeUser(user){
var ind=roomUser.indexOf(user);
if(ind!=-1)
{
roomUser.splice(ind,1);
application.broadcastMsg("userList",roomUser);
}
}
function getUserByUserName(userName){
for(var i=0;i<roomUser.length;i++)
{
if(roomUser[i].userName==userName)
{
return roomUser[i];
}
}
return null;
}
function getUserByClient(client){
for(var i=0;i<roomUser.length;i++)
{
if(roomUser[i].client==client)
{
return roomUser[i];
}
}
return null;
}
function getUserById(id){
for(var i=0;i<roomUser.length;i++)
{
if(roomUser[i].id==id)
{
return roomUser[i];
}
}
return null;
}
client code:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" applicationComplete="application1_applicationCompleteHandler(event)"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import flash.net.NetStream;
import flash.utils.setTimeout;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexEvent;
private var server_ip:String="rtmp://localhost/chat";
private var mineNc:NetConnection;
private var mineNs:NetStream;
private var mineVideo:Video;
private var mineCamera:Camera;
private var mineMic:Microphone;
private const cameraW:Number=400;
private const cameraH:Number=300;
private var targetVideo:Video;
private var targetNs:NetStream;
private var STATE_MODE_NOTPRIVATE:String="notPrivate";
private var STATE_MODE_ISPRIVATE:String="isPrivate";
[Bindable]
private var user:Object;
[Bindable]
private var isLoign:Boolean=false;
[Bindable]
private var userArr:ArrayCollection=new ArrayCollection();
//跟server對應的狀態變數
private const INVITE_RESULT_PRIVATE:int=0;
private const INVITE_RESULT_SUCCESS:int=1;
private const INVITE_RESULT_OFF_LINE:int=2;
private const INVITE_RESULT_REJECT:int=3;
protected function application1_applicationCompleteHandler(event:FlexEvent):void
{
chat_input.addEventListener(KeyboardEvent.KEY_UP,onChatKeyUp);
}
private function login():void
{
var name:String=name_txt.text;
if(name!="")
{
mineNc = new NetConnection();
mineNc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
mineNc.connect(server_ip,name);
login_group.visible=false;
var ncClient:Object = new Object();
mineNc.client = ncClient;
ncClient.systemMessage = systemMessage;
ncClient.userList = userList;
ncClient.chatReceive = chatReceive;
ncClient.onInvite = onInvite;
ncClient.hasInvite = hasInvite;
ncClient.intoPrivate = intoPrivate;
ncClient.loginSuccess = loginSuccess;
ncClient.onExitPrivate = onExitPrivate;
}
else
Alert.show("請輸入登入別名", "ERROR", Alert.OK,null);
}
private function onExitPrivate():void
{
this.currentState=STATE_MODE_NOTPRIVATE;
mineNs.attachCamera(null);
}
private function loginSuccess(user:Object):void
{
this.user=user;
}
private function intoPrivate(streamId:String,targetStreamId:String):void
{
systemMessage("my straemId:"+streamId+",target StreamId:"+targetStreamId);
this.currentState=STATE_MODE_ISPRIVATE;
publishLiveStream(streamId);
this.addRemoteVideo(targetStreamId);
}
private function publishLiveStream(streamName:String):void {
mineNs = new NetStream(mineNc);
mineNs.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
mineCamera = Camera.getCamera();
mineMic = Microphone.getMicrophone();
if (mineCamera != null){
mineCamera.setQuality(0,100);
mineCamera.setMode(cameraW,cameraH,28);
mineCamera.setKeyFrameInterval(60);
mineCamera.addEventListener(ActivityEvent.ACTIVITY, activityHandler);
mineVideo = new Video();
mineVideo.attachCamera(mineCamera);
mineVideo.width=cameraW;
mineVideo.height=cameraH;
mineVideo.smoothing = true;
mineNs.attachCamera(mineCamera);
mineVideoDisplay.addChild(mineVideo);
}
if (mineMic != null) {
mineMic.addEventListener(ActivityEvent.ACTIVITY, activityHandler);
mineNs.attachAudio(mineMic);
}
if (mineCamera != null || mineMic != null){
mineNs.publish(streamName, "live");
} else {
trace("Please check your camera and microphone");
}
}
private function activityHandler(event:ActivityEvent):void {
trace("activityHandler: " + event);
trace("activating: " + event.activating);
}
private function addRemoteVideo(streamName:String,id:String="",name:String=""):void{
targetNs=new NetStream(mineNc);
targetNs.play(streamName);
targetVideo = new Video();
targetVideo.width=cameraW;
targetVideo.height=cameraH;
targetVideo.attachNetStream(targetNs);
targetVideo.smoothing = true;
tagetVideoDisplay.addChild(targetVideo);
tagetVideoDisplay.visible=true;
}
private function hasInvite(id:String,userName:String):void
{
Alert.show(userName+"邀請你進入視訊聊天,是否接受?", "hasInvite", Alert.OK|Alert.NO,null,addInviteArguments(onHasInvite,id));
//這裡有一個特別的用法,學會它,你可以在限定參數的event function內,帶入各種想要的參數
}
private function addInviteArguments(method:Function, id:String):Function
{
return function(event:Event):void {method.apply(null, [event].concat(id));}
}
private function onHasInvite(e:CloseEvent,id:String):void{
mineNc.call("sendInviteResponse",null,id,Boolean(e.detail==Alert.OK));
}
private function onInvite(resultCode:int):void
{
switch(resultCode)
{
case INVITE_RESULT_PRIVATE:
Alert.show("對方已在視訊聊天中", "onInvite", Alert.OK,null);
break;
case INVITE_RESULT_SUCCESS:
Alert.show("等待對方回應中...", "onInvite", Alert.OK,null);
break;
case INVITE_RESULT_OFF_LINE:
Alert.show("對方已不在線上", "onInvite", Alert.OK,null);
break;
case INVITE_RESULT_REJECT:
Alert.show("對方捥拒你的邀請", "onInvite", Alert.OK,null);
break;
default:
Alert.show("未知錯誤", "onInvite", Alert.OK,null);
break;
}
}
private function chatReceive(senderName:String,msg:String):void
{
msg=senderName+":"+msg+String.fromCharCode(13);
content_txt.text+=msg;
}
private function netStatusHandler(event:NetStatusEvent):void
{
trace(event.info.code);
switch (event.info.code)
{
case "NetConnection.Connect.Success":
isLoign=true;
break;
case "NetConnection.Connect.Closed":
isLoign=false;
Alert.show("Closed", "ERROR", Alert.OK,null);
break;
case "NetConnection.Connect.Rejected":
isLoign=false;
loginFail(event.info.application);
break;
}
}
private function loginFail(type:int):void{
switch(type)
{
case 0:
Alert.show("重覆登入", "ERROR", Alert.OK,null);
break;
default:
Alert.show("Rejected", "ERROR", Alert.OK,null);
break;
}
}
private function onChatKeyUp(evt:KeyboardEvent):void
{
if (evt.keyCode == Keyboard.ENTER)
{
mineNc.call("chatToAll", null,chat_input.text);
chat_input.text="";
}
}
private function systemMessage(msg:String):void
{
msg+=String.fromCharCode(13);
content_txt.text+=msg;
}
private function userList(list:Object):void
{
userArr=new ArrayCollection;
for(var i:int=0;i<list.length;i++)
{
userArr.addItem(list[i]);
}
}
private function invite():void
{
if(room_list.selectedIndex<0)return;
var user:Object=userArr.getItemAt(room_list.selectedIndex);
if(user.id==this.user.id)
{
Alert.show("不能邀請自已", "ERROR", Alert.OK,null);
return;
}
if(this.currentState==STATE_MODE_ISPRIVATE)
{
Alert.show("你已在private中", "ERROR", Alert.OK,null);
return;
}
if(user)
{
mineNc.call("invite", null,user.id);
}
}
private function exitPrivate():void
{
mineNc.call("exitPrivate", null);
onExitPrivate();
}
]]>
</fx:Script>
<s:states>
<s:State name="notPrivate" />
<s:State name="isPrivate" />
</s:states>
<s:HGroup width="100%" height="100%">
<s:VGroup width="20%" height="100%">
<s:List id="room_list" width="100%" height="95%" dataProvider="{userArr}" itemRenderer="userItem" verticalScrollPolicy="auto" horizontalScrollPolicy="off" contentBackgroundAlpha="0.0" borderVisible="false"/>
<s:HGroup width="100%" height="5%">
<s:Button label="邀請視訊" click="invite()" enabled="{isLoign}" />
</s:HGroup>
</s:VGroup>
<s:VGroup width="80%" height="100%">
<s:HGroup width="100%" height="100%" includeIn="isPrivate">
<mx:VideoDisplay id="tagetVideoDisplay" width="{cameraW}" height="{cameraH}"/>
<mx:VideoDisplay id="mineVideoDisplay" width="{cameraW}" height="{cameraH}"/>
<s:Button label="exit" click="exitPrivate()"/>
</s:HGroup>
<s:TextArea editable="false" id="content_txt" width="100%" height="90%"/>
<s:TextInput id="chat_input" width="100%" height="10%"/>
</s:VGroup>
</s:HGroup>
<s:HGroup id="login_group" right="10" top="10">
<s:TextInput width="100" id="name_txt"/>
<s:Button label="login" click="login()"/>
</s:HGroup>
</s:Application>


下一次,來增加在server上呼叫php api的功能,順便補上一對一密語的功能。~