存档

文章标签 ‘fms3’

(译)Learning Flash Media Server 3中文版-第7章之4/5

2009年2月5日 评论已被关闭

到目前为止,File.write()是唯一用来写数据的方法,File.readAll()是唯一用来读取文件内容的方法。可是,在服务端你仍然有更多的选择。表7-1中显示了File类中所有写和读的方法。

表7-1

File 方法 说明
write(p) 把每一个参数转换成一个字符串, 写入一个文件
writeAll(arr) 把一个数组参数写入打开的文件,并在数组中的每一个元素后面加上换行(我使用汉字的时候只写入了第一个元素,后面的写不进去,不知道是不是bug,后面再来研究一下)
writeByte(n) 把一个字节写入一个文件
writen(p) 把数据写入到一个文件并在最后一个参数的后面添加一个与平台有关的行尾字符
read(n) 从一个文件中读取n个字符数并返回一个字符串
readAll( ) 从文件指针的位置后读取文件并返回一个数组,数组中的每一个元素对应文件中的每一行
readByte() 从这个文件中读取下一个字节并返回下一个字节的数字值
readln() 从这个文件中读取下一行并作为一个字符串返回
   

使用File.write(),FMS3只是简单地把数据写入文件,传递给客户端。使用File.writeln(),FMS3添加了一个换行符。意思就是在客户端脚本中你不需要添加\n符,当数据发送到服务端时。

In reading the data back, instead of sending an array with a single statement as in the File.readAll() method, File.readln() reads the file a line at a time as a string. When you’re searching for a term in a file, reading a single line at a time can be handy if you have a search word on each line, such as a customer number or name. Then you can extract the single line and send it to the client-side for viewing.

First, use File –> Save As to save the fileIt.asc file as fileEof.asc. Then change the write and read functions to the following and save it as fileIt.asc:

Code View:

 fileWriter.WriteNow = function(cliMsg) { sandFile.open("text", "append"); if (sandFile.open) { sandFile.writeln(cliMsg); sandFile.close( ); } trace(cliMsg); };

 fileWriter.ReadNow = function( ) { sandFile.open("text", "read"); var contentNow = new Array(); if (sandFile.isOpen) { var counter =0; while(!sandFile.eof()) { readBuf=sandFile.readln(); contentNow[counter]=readBuf; counter++; } return contentNow; sandFile.close( ); } };

};

	

The major difference in these functions is employment of the File.eof() method. Using File.readAll() places the entire contents of the file into an array. In this case, the File.eof() function first iterates through the lines in the file and then puts the elements into the array one at a time until the end-of-file condition is met.

7.4.1. 服务端数据初始化

假设你想要找出谁在使用你的应用程序的信息,想要这些从服务端的登录信息中被初始化。当用户登录到你的应用程序中,通常它们会输入一个用户名,如果想要把登录的用户名和用户的IP地址放到一个文件中,这是可以办到的。如果你觉得某个用户有问题,你可以使用IP地址来拒绝他连接到应用程序。所有这些都可以在服务端完成。请参考下面的代码:

  1. 在服务端,建立一个叫fileIP的目录

  2. 在fileIp目录下建立一个fileIP.asc 文件

  3. 在fileIP.asc 文件中输入以下代码

Example 7-4. fileIP.asc
application.onAppStart = function(){ trace("Trap IP is up");};application.onConnect = function(client, name){ application.acceptConnection(client); client.id = name; var ip = client.ip; var ipFile = new File("ip.txt"); ipFile.open("text", "append"); if (ipFile.isOpen) { ipFile.writeln(client.id, ip); ipFile.close(); } trace(client.id+"'s IP address is "+ip);};application.onDisconnect = function(client){ trace(client.id+" has left.");};

Application.onConnect()中触发打开文件和记录当前的用户名名和IP地址。

客户端代码如下:

Example 7-5. IPfile.as

Code View:

package{ import flash.net.NetConnection; import flash.events.NetStatusEvent; import flash.events.MouseEvent; import flash.display.Sprite; import fl.controls.Button; import fl.controls.TextInput;

 public class IPfile extends Sprite { private var nc:NetConnection; private var textInput:TextInput; private var logBtn:Button; private var rtmpNow:String; private var good:Boolean;

 public function IPfile () { nc=new NetConnection(); nc.addEventListener (NetStatusEvent.NET_STATUS,checkConnect); rtmpNow="rtmp://192.168.0.11/fileIP/getIP/"; textInput=new TextInput(); textInput.x=20, textInput.y=20; addChild (textInput);

 logBtn=new Button(); logBtn.x=20, logBtn.y=45; logBtn.width=120; logBtn.label="Enter name and click"; logBtn.addEventListener (MouseEvent.CLICK, logOn); addChild (logBtn); }

 private function logOn (e:MouseEvent) { nc.connect (rtmpNow,textInput.text); }

 private function checkConnect (e:NetStatusEvent):void { good=e.info.code == "NetConnection.Connect.Success"; if (good) { textInput.text="IP Stored"; } else { textInput.text="Failed Connection"; } } }}

The user enters his or her name and clicks the Logon button, labeled Enter Name And Click. This sends the name of the user to the server-side script as the second argument in the server-side line:

application.onConnect = function(client, name) {}

Creating a client.id property with the name passed from the client side and a variable, ip with the value from the client.ip property places both into the ip.txt file using File.writln()., Figure 7-5 shows how the values of two different names from two different connections on a LAN are stored.

Figure 7-5. User names and IP addresses stored in file

注意:当在File.writeln()方法中使用多个参数,值都会被放在单独的行中。当取得数据时,只需要使用其中的一种read方法。

(译)Learning Flash Media Server 3中文版-第7章之3/5

2009年2月5日 评论已被关闭

7.3. 客户端格式化

在客户端和服务端脚本中,数据会自动被格式化。到目前为止,返回到客户端的数据是一个数组。因为当使用File.readAll()方法时,元素由文件中的每一行组成。当数据进入到客户端,被添加到行的\n换行符,用来做分隔行用,从来转为一个数组。现在你知道为什么前面的应用程序中为什么有逗号了吧?(返回上一章看看吧)。只要你打印整个数组时,逗号总是会出现的。可以用下面方法格式化:

private function showFile (fileBack:Array):void
{
  if (fileBack != null)
  {
        var backLen:uint=fileBack.length;
        for (var c:uint; c< backLen; c++)
        {
             textArea.appendText (fileBack[c] + "\n");
        }
  }
  else
  {
        textArea.text="File empty";
  }

通过把结果参数(fileBack)声明为一个数组,设计一个把服务端对像干净的转换为客户端的方法。虽然在服务端不能声明类型,但是在客户端可以。你会看到,每个条目都单独一行的输出,图7-4:

Figure 7-4. Array-formatted output

 

数据是如何输入到文件中的和使用哪种File方法来读取数据,决定了在客户端如何显示。前面计划好,以避免数据不符。

(译)Learning Flash Media Server 3中文版-第7章之2/5

2009年2月4日 评论已被关闭
7.2. File 类

File类用来储存数据库非常方便,不需要使用中间物件或者一个SQL数据库。你可以指定你想要什么,把它写到文件中,然后当需要的时候显示它。File类建立的文件不是共享对像,在这些文件中储存的信息可以被任何客户端访问。

使用File类,涉及到两个基本功能:写数据和读数据。在服务端类中,大部分繁重的工作由服务器担当。客户端的工作就是显示数据。到目前为止,你已经知道怎么样调用一个服务端方法和传递数据。现在,你将学习怎么样从服务端找回数据。第一步,你将在服务端学习怎么样初始化一个文件和写入数据。

7.2.1. 建立一个 File 类的实例

建立一个file实例需要一个参数作为其名。

var myBigFile = new File("myStuff.txt");
上面的代码建立一个实例中为 myBigFile 和一个名为 myStuff.txt 的文本文件。
7.2.2. Writing to a File

为了在一个文件中写数据,使用File.open 方法打开来 write,append,read,使用text,binary,UTF8。

打开通常格式是:

fileInstance.open("text", "write");

一旦文件打开,我们就可以写数据了,写的格式如下:

fileInstance.write(data);


data参数可以是任何东西,代表性的是一个字符串或者数字。在你写这个文件之前,一个好的习惯就是使用 File.isOpen 方法来验证文件是否打开。如果文件是打开的返回true,否则返回false。一旦你已写入数据,下一步就是关闭它,一个典型的顺序如下:

if(fileInstance.isOpen){
  fileInstance.write(data);
  fileInstance.close( );
}

如果你在打开文件时用"append"代替"write",指针会转到文件的末尾。如果你选用"write",会重写整个文件。

7.2.3. Reading a File

读一个文件的时候与写一个文件只有一点不同,把"wirte"改成 "read" 。读的方法有几种:

fileInstance.read(n)从文件中读取n个字符
fileInstance.readAll()读取整个文件
fileInstance.readByte()从这个文件中读取下一个字节并返回下一个字节的数字值,这个方法只适用于文件是以二进制方式打开的文件。
fileInstance.readln( )从这个文件中读取下一行并作为一个字符串返回。行分隔符字符(Windows上的\r\n或Linux上的\n)


一个典型的读文件的顺序如下::

fileInstance.open("text", "read");
if(fileInstance.isOpen){
var myData=fileInstance.readAll( );
fileInstance.close( );  return myData;}

与写的顺序很相似,但是一个新的元素需要说明一下,就是 return 语句。(我想大家都应该明白吧,略过了哦……)

Example 7-1. fileIt.asc

Code View:

application.onAppStart=function( ){ trace("Basic File script is up");};

application.onConnect=function(fileWriter, name){
fileWriter.id = name;
application.acceptConnection(fileWriter);
var sandFile = new File("sandlight.txt");

//写入
fileWriter.WriteNow = function(cliMsg) {
sandFile.open("text", "append");
if (sandFile.isOpen) {
sandFile.write(cliMsg);
sandFile.close( );
}
};
//读取
fileWriter.ReadNow = function( ) {
sandFile.open("text", "read");
if (sandFile.isOpen) {
contentNow = sandFile.readAll( );
sandFile.close( );
return contentNow; } };};

application.onDisconnect=function(fileWriter){ trace(fileWriter.id+" has left.");}

完成以上代码,后面你就需要一个客户端脚本来输入数据到文件中和从文件中读取数据。

7.2.4. Minimal 客户端写和读数据

所有你在客户端上真正需要的基本操作就是与写数据到文件和读取数据有关的。发送数据到服务端函数和捕捉返回的数据。从前面的章节中,你知道NectConnection.call()方法,可以发送参数到服务端。然而从服务端返回数据却是另一回事。幸运的是,它并不困难。紧记NetConnection.call()方法有三个参数:服务端函数名、Responder对像(捕获从服务端返回什么)、还有一个可选参数,用来发送数据到服务端。所有你需要做的就是把一个参数发送过去写入和调用别一个方法读取。

关于用NetConnection.call()发送数据已经在第六章中讨论过。在这里,你需要用NetConnection.call()来从服务端返回数据。基本格式如下:

NetConnection.call("readDataFunc",responder);

第二个参数responder用来捕获呼叫的函数返回的值。

图7-1显示了客户端呼叫和返回的值之间的关系。

Figure 7-1. Client-side calls and server-side returns

图7-1说明了几个几点:

  • 客户端脚本中61行的writeFile 函数,调用了服务端脚本中12行的WriteNow 函数。writeFile 函数有一个null参数作为回应,发送TextInput对像的文本给服务端。
  • 在服务端脚本中,WriteNow 函数捕获cliMsg中的内容。这个内容会被写到文件中,通过sandFile.write(cliMsg)
  • 客户端脚本中的readFile 函数(67行)呼叫服务端中的ReadNow 函数。脚本中定义了一个Responder 实例,用来发送任何返回的数据到ReadNow (73行)函数中。
  • ReadNow 函数触发,它打开文件,然后把返回的文件中的内容赋给一个变量contentNow 。在客户端的showFile函数(73行)中, 返回的内容被储存在一个字符串参数fileBack中,

现在开始建立客户端应用程序,看看最少的脚本,File类能够做什么:

  1. Open a new Flash file (ActionScript 3.0) and save it as BasicFile.fla.
  2. Open the Library panel, and from the Components panel drag in Button, TextInput and TextArea components.
  3. In the Document Class text box in the Property inspector, type BasicFile and resave the file.
  4. Open a new ActionScript file and save it as BasicFile.as in the same folder with the BasicFile.fla file. (These files should not be in the same folder with your server-side applications!)
  5. In the BasicFile.as file, enter the code in Example 7-2 and save the file.
Example 7-2. BasicFile.as

Code View:

package
{
  import flash.net.NetConnection;
  import flash.net.Responder;
  import flash.events.NetStatusEvent;
  import flash.events.MouseEvent;
  import flash.display.Sprite;
  import fl.controls.Button;
  import fl.controls.TextInput;
  import fl.controls.TextArea;

  public class BasicFile extends Sprite
  {
        private var nc:NetConnection;
        private var textInput:TextInput;
        private var textArea:TextArea;
        private var readBtn:Button;
        private var writeBtn:Button;
        private var rtmpNow:String;
        private var good:Boolean;
        private var responder:Responder;

        public function BasicFile ()
        {
             nc=new NetConnection();
             rtmpNow="rtmp://192.168.0.11/fileIt/storeFile";
             nc.connect (rtmpNow,"Writer");
             nc.addEventListener (NetStatusEvent.NET_STATUS,checkConnect);
             writeBtn=new Button();
             writeBtn.x=20, writeBtn.y=45;
             writeBtn.width=60;
             writeBtn.addEventListener (MouseEvent.CLICK,writeFile);
             addChild (writeBtn);

             readBtn=new Button();
             readBtn.x=85, readBtn.y=45;
             readBtn.width=60;
             readBtn.addEventListener (MouseEvent.CLICK,readFile);
             addChild (readBtn);

             textInput=new TextInput();
             textInput.x=20, textInput.y=20;
             addChild (textInput);

             textArea = new TextArea();
             textArea.width=100, textArea.height=200;
             textArea.x=20, textArea.y=70;
             addChild (textArea);
        }

        private function checkConnect (e:NetStatusEvent):void
        {
             good=e.info.code == "NetConnection.Connect.Success";
             if (good)
             {
                   writeBtn.label="Write File";
                   readBtn.label="Read File";
             }
        }

        private function writeFile (e:MouseEvent):void
        {
             nc.call ("WriteNow",null,textInput.text+"\n");
             textInput.text="";
        }

        private function readFile (e:MouseEvent):void
        {
             responder=new Responder(showFile);
             nc.call ("ReadNow",responder);
        }

        private function showFile (fileBack:String):void
        {
             textArea.appendText (fileBack);
        }
  }
}					  

当你测试这个应用程序时,输入不同的词语,按下Write按惯。一旦你输入了多个词语后,按下Read按纽,你的完整的文件内容如下:

Figure 7-2. Displaying data from a server-side file

看看上面输出的,你可能会看到,储存在在文件中的所有的换行都丢失了,如下图所示:

Figure 7-3. Text file automatically created with File class

看上面的图,没有格式化是因为file格式。在后面的章节中,你将学习一些格式化的方法和使用服务端 File类和As3.0来更好的格式化输出。首先,你将考虑如何消除所有内容和在文件中输入一些别的东西。

7.2.5. Deleting Files

体验上面的程序,你将会看到文件的内容会不断增加。有没有一种方法可以删除文件中的内容,重新开始呢?有一种方法就是删除文件本身。使用File.remove():

fileInstance.remove( );


把它加到服务端,这样你就可以从客户端调用了:

fileWriter.RemoveNow = function( ){ sandFile.remove( );};

保存服务端脚本为fileItRemove.asc,放置在fileItRemove目录下,然后跟随以下步骤编辑客户端文件:

  1. Open the client-side BasicFile.fla file and change the Document class value in the Property inspector to BasicRemove and save the file as BasicRemove.fla.
  2. Open the BasicFile.as file and save it as BasicRemove.as.
  3. In the BasicRemove.as file, replace the script with that shown in Example 7-3. Save the file again.

Example 7-3. BasicFileRemove.as

Code View:

package
{
  import flash.net.NetConnection;
  import flash.net.Responder;
  import flash.events.NetStatusEvent;
  import flash.events.MouseEvent;
  import flash.display.Sprite;
  import fl.controls.Button;
  import fl.controls.TextInput;
  import fl.controls.TextArea;

  public class BasicRemove extends Sprite
  {
        private var nc:NetConnection;
        private var textInput:TextInput;
        private var textArea:TextArea;
        private var readBtn:Button;
        private var writeBtn:Button;
        private var removeBtn:Button;
        private var rtmpNow:String;
        private var good:Boolean;
        private var responder:Responder;

        public function BasicRemove ()
        {
             nc=new NetConnection();
             rtmpNow="rtmp://192.168.0.11/fileItRemove/storeFile";
             nc.connect (rtmpNow,"Writer");
             nc.addEventListener (NetStatusEvent.NET_STATUS,checkConnect);
             writeBtn=new Button();
             writeBtn.x=20, writeBtn.y=45;
             writeBtn.width=60;
             writeBtn.addEventListener (MouseEvent.CLICK,writeFile);
             addChild (writeBtn);

             readBtn=new Button();
             readBtn.x=85, readBtn.y=45;
             readBtn.width=60;
             readBtn.addEventListener (MouseEvent.CLICK,readFile);
             addChild (readBtn);

             removeBtn=new Button();
             removeBtn.x=145, removeBtn.y=45;
             removeBtn.width=90;
             removeBtn.addEventListener (MouseEvent.CLICK,removeFile);
             addChild (removeBtn);

             textInput=new TextInput();
             textInput.width=125;
             textInput.x=20, textInput.y=20;
             addChild (textInput);

             textArea = new TextArea();
             textArea.width=125, textArea.height=200;
             textArea.x=20, textArea.y=70;
             addChild (textArea);
        }

        private function checkConnect (e:NetStatusEvent):void
        {
             good=e.info.code == "NetConnection.Connect.Success";
             if (good)
             {
                   writeBtn.label="Write File";
                   readBtn.label="Read File";
                   removeBtn.label="Remove File";
             }
        }

        private function writeFile (e:MouseEvent) :void
        {
             nc.call ("WriteNow",null,textInput.text+"\n");
             textInput.text="";
        }

        private function readFile (e:MouseEvent):void
        {
             responder=new Responder(showFile);
             nc.call ("ReadNow",responder);
        }

        private function removeFile (e:MouseEvent)
        {
             nc.call ("RemoveNow",null);
        }

        private function showFile (fileBack:String):void
        {
             if (fileBack != null)
             {
                   textArea.appendText (fileBack);
             }
             else
             {
                   textArea.text="Empty File";
             }
        }
  }
}

注意:

打开服务端fileItRemove目录,然后运行应用程序。一旦你写入数据,文本文件就会出现,然后,点击删除按纽,它就会不见了。其它这个指令不是真正的从文件中删除数据,而是删除文件本身。

(译)Learning Flash Media Server 3中文版-第7章之1/5

2009年2月4日 评论已被关闭
7.1. 录制数据

这章探讨使用FMS3来录制数据。我们来看看服务端的File类。这个类允许开发者建立应用程序时,把信息储存在一个文本文件中,也可以是二进制和UTF-8文件,在服务端的一个安全位置。

注意:

在你使用服务端脚本的时候,你将看到客户端(as3)和服务端脚本(SSAS)之间有几个不同的地方。as3符合ECMAScript 4标准,SSAS是FMS附加的一些JavaScript类。最重要的是使用数据类型-描述你的对像的数据类型。在as3.0中,在参数或者变量后面加上(:)来描述数据类型,如下:

private var myList: Array=new Array("A", "B", "C");
protected var manyWords:String;
public function shedArea(H:Number, W:Number):Number
private function getData( ) :void ….

你只要在客户端代码中养成数据类型的习惯,记住,服务端的数据是不需要类型的。

(译)Learning Flash Media Server 3中文版-第6章之3/7

2008年10月8日 评论已被关闭

建立一个最简单的广播工作室是一个很简单的操作,要求只不过是一个模块与送出现场的音频和视频流。为了使它更容易,特别是在最后,这种应用程序包括一个可重用的类,只不过是为设置流出和流入的流。这个类工作起来,就像一个实用工具类,您可以重新使用,以简化创建流式音频/视频的工作。

6.3.1.公用流类

第一步是建立公用类让音频和视频流可以简单的工作。它使用以下列几类:

Classes
NetConnection
NetStream
Camera
Microphone
Video

这个公用类是扩展NetStream类,这个类有两个公共的方法:一个是流a/v出去(streamOut),一个是流进来(streamIn)。记住,一个流需要一个麦克风实例、一个摄像头实例、一个流的名称。streamOut方法的参数就是这三个元素。

反过来,要播放进来的视频,所有你所需要的是视频对象的名称和流的名称。

因为这首先是最低限度的应用,只有视频流出来,您可能感到奇怪,为什么理会streamIn方法呢?这个类的目的是:不是让一个应用程序更好的开发,而是多个。

在稍后的章节,您将了解如何创造一个“receiver”,像电视机一样工作,而且您将高兴地看到, streamIn方法包括在内。

最后,一个元素你可能不熟悉,是super语句。此公用类是扩展NetStream类。创造一个NetStream实例,你需要包含NetConnection参数。Super语句调用父类的NetConnection,所以,当你建造一个StreamAV实例,您可以包含必要的NetConnection参数。按照下列步骤创建此公用类。

  1. 建立一个StreamAV.as.
  2. 添加以下的代码

Example 6-2. StreamAV.as

package
{
  import flash.net.NetConnection;
  import flash.net.NetStream;
  import flash.media.Camera;
  import flash.media.Microphone;
  import flash.media.Video;
  public class StreamAV extends NetStream
  {
        public function StreamAV (nc:NetConnection)
        {
             super (nc);
        }
        public function streamOut (mic:Microphone,cam:Camera,stream:String):void
        {
             this.attachAudio (mic);

             this.attachCamera (cam);

             this.publish (stream,"live");

        }
        public function streamIn (vid:Video,stream:String):void
        {
             vid.attachNetStream (this);
             this.play (stream);
        }
  }
}
6.3.2.基本的播出演播室

第一个Studio模块非常简单。只是显示"TV Anchor"(你)和”on the air”消息,让你知道你已经连接了。第5章中展示了怎么流进来和流出去。这个模块是简单的流出去-使用StreamAV类帮忙。

Classes

NetConnection

NetStatusEvent

Camera

Microphone

Video

Sprite

TextField

StreamAV

为什么NetStream类没有列在这个应用程序中?因为你将用新的公用类StreamAV处理。在Broadcast和TV 两个应用程序中,使用StreamAV类。

以下介绍建立一个studio的最少步骤:

1. 在MS3的applications目录中建立一个名为studio的目录

2. 建立一个Broadcast.fla与StreamAv.as同一目录

3. 可选,用一个照片来作为studio的背景,或者用flash画一个。使用矩形工具在背景上面画一个矩形,宽200高150,位置为x=150,y=190。这将作为监控屏幕。在左上角的位置,添加了第二个矩形与尺寸为W = 75 ,为H = 21 ,为Y = 21 ,为Y = 68.5 。图6-1显示了总体思路,和图6-2显示什么,您会看到当您执行该脚本。

图6-1. Studio backdrop

clip_image002

4. 建立一个Broadcast.as与StreamAV.as和 Broadcast.fla同一目录

5. 在Broadcast.as文件中添加以下代码

Example 6-3. BroadCast.as

package
{
  import flash.net.NetConnection;
  import flash.events.NetStatusEvent;
  import flash.media.Camera;
  import flash.media.Microphone;
  import flash.media.Video;
  import flash.display.Sprite;
  import flash.text.TextField;
  public class Broadcast extends Sprite
  {
        private var nc:NetConnection;

        private var cam:Camera;

        private var mic:Microphone;

        private var vid:Video;

        private var rtmpNow:String;

        private var onAir:String;

        private var broadcast:StreamAV;

        private var good:Boolean;

        private var txtField:TextField;
        public function Broadcast ()

        {

             //Set Camera and Microphone
             nc=new NetConnection ;

             nc.addEventListener (NetStatusEvent.NET_STATUS,checkConnect);

             cam=Camera.getCamera();

             cam.setMode(240,180,15);

             cam.setQuality(0,85);

             mic=Microphone.getMicrophone();
             mic.rate=11;

             vid=new Video(240,180);
             vid.x=150;
             vid.y=90;
             addChild(vid);

             txtField=new TextField();

             txtField.x=150;

             txtField.y=75;

             addChild(txtField);

             rtmpNow="rtmp://192.168.0.11 /studio";

             nc.connect (rtmpNow,"anchor");

        }
        //Connection

        private function checkConnect (e:NetStatusEvent)
        {
             good=e.info.code == "NetConnection.Connect.Success";

             if (good)
             {

                   txtField.text="On The Air";

                   broadcast=new StreamAV(nc);

                   vid.attachCamera (cam);

                   broadcast.streamOut (mic,cam,"presentation");
             }
        }
  }
}

当你运行这个模块,你将会看到你自已在监视器中和一个"on the air"信息指示你已连接到服务器了。这个单个的流从你当前的摄像头和麦克风中发出。

图6-2. Broadcast studio

clip_image004

你现在有一个广播室,您需要一个模块来接收串流的材料。这基本上是电视,用户可以观赏到您的视频。

6.3.3.最简单TV

如果您认为最简单工作室是很容易,最简单TV会更容易。所有您需要的是一个视频对象在舞台和您良好的路要走。这些最基本元素,是您需要的类:

Classes

NetConnection

NetStatusEvent

Video

Sprite

StreamAV

像studio,你可以简单的制作你的TV或者精心的制作。我使用了一个老式的TV作为背景,然而在其它方面,它是简单地完美,按以下这些步骤来建立TV:

  1. 建立一个fla文件,舞台尺寸为360X350
  2. 你可以把舞台设置更小,但是不能小于215X138。小于那个尺寸的话,就会小于设置窗口,不能工作了。对于这个特别的应用程序,不要让它小于215X150,因为你的视频大小是200X150。还是把它设大点的好。我是基于TV图像的尺寸来定制这个尺寸的。
  3. 保存TV.fla与StreamAV.as文件同一目录。如果你想把TV和Broadcast模块分开来在不同的目录中,也可以,确保每个目录包含Stream.as文件
  4. 可选,建立一个TV背景,如图6-3所示

图6-3. Stage for TV module

clip_image002[4]

5. 建立一个TV.as文件,与TV.fla同一目录

6. 在TV.as文件中添加以下代码

Example 6-4. TV.as

package
{
  import flash.net.NetConnection;

  import flash.events.NetStatusEvent;

  import flash.media.Video;

  import flash.display.Sprite;

 

  public class TV extends Sprite

  {

        private var nc:NetConnection;

        private var vid:Video;

        private var rtmpNow:String;

        private var tvSet:StreamAV;

        private var good:Boolean;

 

        public function TV()

        {

             nc=new NetConnection ;

             nc.addEventListener (NetStatusEvent.NET_STATUS,checkConnect);

             vid=new Video(200,150);

             vid.x=81;

             vid.y=76;

             addChild (vid);

             rtmpNow="rtmp://192.168.0.11 /studio";

             nc.connect (rtmpNow,"viewer");

        }

 

        //Connection

        private function checkConnect (e:NetStatusEvent)

        {

             good=e.info.code == "NetConnection.Connect.Success";

             if (good)

             {

                   tvSet=new StreamAV(nc);

                   tvSet.streamIn (vid,"presentation");

             }

        }

  }

}

当你完成,测试你的广播系统。首先,加载Broadcast模块和运行它。一旦显示“on the air”,你可以准备运行TV模块了。如果你的TV显示从Studio模块广播过来的图像,每个东西都在工作中。同样地,你可也可以听到从stiduo发过来的声音。

图6-4. Studio stream seen on TV module

clip_image004[4]

你可能注意到了,在脚本中NetConnection.connect()的参数包含一个额外的值。直到现在,你看到在connect()方法中是应用程序的名称(例如,studio)。两个模块都有一个可选的参数。在这里,这个可选参数指示了用”anchor”或者”viewer” 作为标记。依赖于哪个模块正在运行。在服务端,你将会在下一节中学习到,这个可选参数(唯一的)帮助指示谁正在使用应用程序。可能你想要限制谁可以和不可以使用你的应用程序,使用第二个参数与用户识别码,可以非常方便。

(译)Learning Flash Media Server 3中文版-第6章之2/7

2008年6月10日 评论已被关闭

(翻译能力有限,仅供个人学习参考,看得懂就好-_-||)

Learning Flash Media Server 3中文版-第6章广播和服务端带宽控制

6.2.切换摄像头

一个在电视演播室工作的任何人都知道,制作人切换摄影机,大部分演出,约有3个不同摄影机拍摄。因此,在从一个摄影机切换到另一个,是设立自己的全球视讯广播公司的一个关键技能。

6.2.1. 选择一个摄像头

像在本书中讨论的多大数改变一样,AS3.0中的一个关键的改变是改变了取得一个摄像头的方法,在以前的as版本中,可以用一个整数作为参数来取得一个摄像头。现在在AS3.0中,用一个字符串。新的方式,仍然使用一个整数类型,当作一个字符串来做这事情。意思就是“4”代替 4 。这个数字是摄像头在设置窗口中的位置。在最上面的是”0”,每一个摄像头是参照其在清单中的相对位置。因此,第五个摄像头是“ 4 ”就像一个基于0的数组。以下一行显示了取得在名单中的第5个摄像头:

var cam2:Camera=Camera.getCamera(“4″);

Camera类的实例:cam2 ,现在是设置窗口的第5个摄像头。 (实际上,使用第5个设置) 。

您可能还记得,如果没有使用参数,摄像头默认为最后选定的摄像头。例如,如果您最近使用的摄像头位置是”5”,下面的会设置为6(如果6在名单中):

var cam1:Camera=Camera.getCamera();

此举意味cam1将取得第六摄像头,而不是在Flash Player设置窗口的第一个-在位置”0” 。

6.2.2. 切换两个摄像头

看看如何切换摄像头,以下应用程序最少需要2个像头。如果你只能有一个摄像头,借用一个或使用一个虚拟网路摄影机,您可以在网上找到。 (一个有趣的摄像头驱动程序对你的屏幕作为一个摄像头,提供您一个好屏幕捕获工具,它可用于Windows :http://www.hmelyoff.com/index.php?section=8)

按以下步骤来建立应用程序:

1. 建立一个CameraSwitch.fla文件

2. 拖一个Button组件到库中

3. 在文档类中输入CameraSwitch

4. 建立一个CameraSwitch.as

5. 在CameraSwitch.as中输入以下代码:

Example 6-1. CameraSwitch.as

Code View:

package
{
    import flash.media.Camera;
    import flash.media.Microphone;
    import flash.media.Video;
    import flash.display.Sprite;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.events.MouseEvent;
    import fl.controls.Button;
 
    public class CameraSwitch extends Sprite
    {
          private var cam1:Camera;
          private var cam2:Camera;
          private var btn1:Button;
          private var btn2:Button;
          private var mic:Microphone;
          private var vid:Video;
 
          public function CameraSwitch ()
          {
               vid=new Video(320,240);
               vid.x=100,vid.y=30;
               addChild (vid);
 
               mic=Microphone.getMicrophone();
               cam1=Camera.getCamera();
               cam1.setMode (320,240,15);
               cam2=Camera.getCamera("4");
               cam2.setMode (320,240,15);
 
               btn1=new Button();
               btn1.width=70;
               btn1.x=vid.x;
               btn1.y=vid.y+vid.height+10;
               btn1.label="Camera 1";
               addChild (btn1);
               btn1.addEventListener (MouseEvent.CLICK,camOne);
 
               btn2=new Button();
               btn2.width=70
               btn2.x=btn1.x+100;
               btn2.y=vid.y+vid.height+10;
               btn2.label="Camera 2";
               addChild (btn2);
               btn2.addEventListener (MouseEvent.CLICK,camTwo);
 
          }
          private function camOne (e:MouseEvent)
          {
               vid.attachCamera (cam1);
          }
          private function camTwo (e:MouseEvent)
          {
               vid.attachCamera (cam2);
          }
    }
}

在测试程序之前,请检查您的Flash Player的设置窗口,并且确保列表中至少两个设备。如果您只有两个设备,确定哪一个是默认的,然后输入字符串数字等。(例如,如果您只有两个设备和第一个是默认的[“0”] ,然后设置cam2为”1” )。

在接下来的Studio应用程序。如果您只有一个单一的摄像头,你大概不会想添加了第二个摄像头。不过,如果您想要把您的studio成为一个更强大的应用程序,那么只需合并Example 6-1的代码。

(译)Learning Flash Media Server 3中文版-第6章之1/7

2008年6月9日 评论已被关闭

(翻译能力有限,仅供个人学习参考,看得懂就好-_-||)

Learning Flash Media Server 3中文版-第6章广播和服务端带宽控制

6.1.铸造许多流

制作一个全世界的广播系统的想法,看起来似乎有点雄心勃勃,但使用FMS3 ,它其实很容易。所有您需要做的只是发出一个包含音频和视频的流,使用” Studio”模块,并有一个“TV”模块set to pick it up。你甚至可以设置带宽分配,所以Studio模块使用更多的客户端到服务器之间的带宽,TV模块是有利的,所以大部分的带宽分配是从服务器到客户端。

此外,您可以在live和录制之间切换和从远程位置取得报告。你走出去,并聘请一名新闻队伍之前,记得每一个客户端连接到应用程序,FMS3生成一个新的流。 FMS3既有的带宽和连接客户端的限制,所以根据您的fms3版本,您将可以服务更多或更少的客户。但是,您可以创建一个广播的应用程序,优化带宽,使您可以服务一样多的客户同时,尽可能的仍提供服务器端的最佳A / V。

(译)Learning Flash Media Server 3中文版-第5章之6/7和7/7

2008年6月8日 评论已被关闭

(翻译能力有限,仅供个人学习参考,看得懂就好-_-||)

Learning Flash Media Server 3中文版-第5章双向音频-视频通信

5.6.四向的会议应用程序

从一个双向聊天应用到多个参加会议的应用是大概的缩放和带宽调整。对服务器端,唯一的要求是,再添加两个元素到数组和更改名称。不过,在实际应用中工作,您需要看看对带宽的要求。

5.6.1. 2 等于2:4等于12

在本章开始之前,图5-1显示了流的数量是如何增长的:双向A/V聊天是2个流,4向A/V聊天是12个流。与流的数量增长,减少对带宽是必不可少的。首先,看看影片发送整个网际网路的尺寸。在Deux应用程序中,相机设置为240 × 180 ,产生大约43.2千比特(千位)像素。乘以帧速度15fps和流(2),应用程序产生大约1.3兆比特( mbits )每秒。这还不包括音频,但现在, 1.3 mbits可被用来作为4路会议的目标带宽。 (音频的一个好的地方是,在大多数的会议,同一时间只有一人会谈,所以在双向或者4向中,其大小可能不会有很大的不同)。

使用1.3 mbits作为目标带宽,首先考虑的是要传输的视频的大小。在Deux应用程序中,两个参与者两旁都有很多空白。切割出额外的水平空间,使得议长清晰可见,和您释放了大量的未使用的bits整个网际网路。 4路会议的应用程序,命名为Quad,削减相机宽度从240至80 。为了更好地集中发言者,高度从180削减到100 。现在,不是一个240 × 180的矩阵,而是80 × 100的矩阵了 。其次,降低fps,从15降到12。做这个简单的算术,bits的数量应该是1.15 mbits –实际上少于2人的聊天。检查下列步骤:

  • a. 80 x 100 = 8,000.
  • b. 8,000 x 12 = 96,000 (where 12 is the number of streams).
  • c. 96,000 x 12 =1,152,000 (where 12 is also the frames per second).

当你完成这些计算,你能够建立一个站点,是不太可能冻结起来并给予客户一个坏的经验

5.6.2.布置客户和他们的流

与4人在一个会议,给他们一个参考框架,帮助他们有一个整体会议意识。为此,应用程序有背景框,就是视频出现的地方。因此,举例来说,如果有两个用户在网站上,他们可以看到后面两个用户出现在哪里。logo是放置在应用程序的中间,暗示在一个圆桌,人人的地位平等。然而,由于观看是在一个垂直平面(电脑显示器) ,人在最上方的位置可能会出现比别人更强大,人在底部在是处于最低的地位。

所幸的是,因为这是一个虚拟会议,放置可以把每个人都在最上方的位置,由用户自己的有利位置。简单地通过发送所有本地视频到最上方的位置,每个用户都可以看到他或她自已在顶部,被其余人环绕着。

要获得此效果,并正确的分发所有的流, switch语句再度派上用场。以下代码段显示了一切是如何分布式的:

Code View:

switch (streamSelect)
{
  case "left" :
        outStream="left";
        inStream1="right";
        inStream2="top";
        inStream3="bottom";
        break;
  case "right" :
        outStream="right";
        inStream1="left";
        inStream2="top";
        inStream3="bottom";
        break;
  case "top" :
        outStream="top";
        inStream1="left";
        inStream2="right";
        inStream3="bottom";
        break;
  case "bottom" :
        outStream="bottom";
        inStream1="left";
        inStream2="top";
        inStream3="right";
        break;
}
 

作为与Deux应用程序,在switch语句中,案例的所有的数据是由服务器端脚本提供的。而不是只二个:leftright,现在streamSelect的值观是left, right, top, 和 bottom。这些名称对不同流的位置不产生影响。而他们只是四个方便的名字。后面的代码段显示了如何分布流:

//Publish local video
netOut=new NetStream(nc);
netOut.attachAudio (mic);
netOut.attachCamera (cam);
vidOut.attachCamera (cam);
netOut.publish (outStream, "live");
 
//Play streamed video
netIn1=new NetStream(nc);
vidBottom.attachNetStream (netIn1);
netIn1.play (inStream1);
 
netIn2=new NetStream(nc);
vidLeft.attachNetStream (netIn2);
netIn2.play (inStream2);
 
netIn3=new NetStream(nc);
vidRight.attachNetStream (netIn3);
netIn3.play (inStream3);

名为vidOut是在顶部位置的video对像,和名为netOut的是从本地客户端的摄像机和麦克风中即将流出的流。其他传入的流分配给左,右,和下而的视频对象,流的名字,是switch语句中产生的。

5.6.3.建立会议应用程序

现在准备开始建立,按以下步骤来建立,首先放置对像在舞台上:

1. 建立一个Quad.fla文件,在文档类中输入Quad

2. 使用图5-8作为一个指南,使用矩形工具在舞台上画4个80 X 100的矩形,位置如图5-8所示。

图5-8. Placement of backdrops

clip_image002

3. 把你的logo放置在背景的中间,Quad文字在顶部,

4. 建立一个Quad.as文件

5. 在Quad.as中输入以下代码

Example 5-4. Quad.as

Code View:

package
{
  import flash.display.Sprite;
  import flash.display.MovieClip;
  import flash.events.NetStatusEvent;
  import flash.net.NetConnection;
  import flash.net.NetStream;
  import flash.media.Camera;
  import flash.media.Microphone;
  import flash.media.Video;
  import flash.net.Responder;
 
  public class Quad extends Sprite
  {
        private var nc:NetConnection;
        private var good:Boolean;
        private var netOut:NetStream;
        private var netIn1:NetStream;
        private var netIn2:NetStream;
        private var netIn3:NetStream;
        private var cam:Camera;
        private var mic:Microphone;
        private var responder:Responder;
        private var vidOut:Video;
        private var vidBottom:Video;
        private var vidLeft:Video;
        private var vidRight:Video;
        private var outStream:String;
        private var inStream1:String;
        private var inStream2:String;
        private var inStream3:String;
 
        public function Quad ()
        {
             var rtmpNow:String="rtmp://192.168.0.11/quad";
             nc=new NetConnection;
             nc.connect (rtmpNow);
             nc.addEventListener (NetStatusEvent.NET_STATUS,getStream);
        }
 
        private function getStream (e:NetStatusEvent):void
        {
             good=e.info.code == "NetConnection.Connect.Success";
             if (good)
             {
                   responder=new Responder(streamNow);
                   nc.call ("streamSelect",responder);
             }
        }
 
        private function streamNow (streamSelect:String):void
        {
             setCam ();
             setMic ();
             setVid ();
 
             switch (streamSelect)
             {
                   case "left" :
                        outStream="left";
                        inStream1="right";
                        inStream2="top";
                        inStream3="bottom";
                        break;
                   case "right" :
                        outStream="right";
                        inStream1="left";
                        inStream2="top";
                        inStream3="bottom";
                        break;
                   case "top" :
                        outStream="top";
                        inStream1="left";
                        inStream2="right";
                        inStream3="bottom";
                        break;
                   case "bottom" :
                        outStream="bottom";
                        inStream1="left";
                        inStream2="top";
                        inStream3="right";
                        break;
             }
 
             //Publish local video
             netOut=new NetStream(nc);
             netOut.attachAudio (mic);
             netOut.attachCamera (cam);
             vidOut.attachCamera (cam);
             netOut.publish (outStream, "live");
 
             //Play streamed video
             netIn1=new NetStream(nc);
             vidBottom.attachNetStream (netIn1);
             netIn1.play (inStream1);
 
             netIn2=new NetStream(nc);
             vidLeft.attachNetStream (netIn2);
             netIn2.play (inStream2);
 
             netIn3=new NetStream(nc);
             vidRight.attachNetStream (netIn3);
             netIn3.play (inStream3);
        }
 
        private function setCam ():void
        {
             cam=Camera.getCamera();
             cam.setMode (80,100,12);
             cam.setQuality (0,80);
             cam.setKeyFrameInterval(12);
        }
 
        private function setMic ():void
        {
             mic=Microphone.getMicrophone();
             mic.rate=11;
             mic.setSilenceLevel (12,2000);
        }
 
        private function setVid ():void
        {
             vidOut=new Video(80,100);
             addChild (vidOut);
             vidOut.x=235;
             vidOut.y=43;
 
             vidLeft=new Video(80,100);
             addChild (vidLeft);
             vidLeft.x=89;
             vidLeft.y=145;
 
             vidRight=new Video(80,100);
             addChild (vidRight);
             vidRight.x=379;
             vidRight.y=145;
 
             vidBottom=new Video(80,100);
             addChild (vidBottom);
             vidBottom.x=235;
             vidBottom.y=253;
        }
  }
}

6. 在你的服务端applications目录中建立一个名为Quad的目录

7. 打开Deux.asc文件,改变第一行

vidStreams=["bottom", "top",right","left"];

8. 把Deux.asc另存为Quad.asc,放置在Quad目录中

9. 发布Quad.fla为html,swf,一旦你把这些文件放置在你的Web服务器上,你已经设置好你的在线会议了。

邀请3位有摄像机和麦克风连的朋友连接到您的应用程序。每一个进入在线会议,他们将看到别人和仍未出现人的绿色背景。如果有人离开会议时,您会看到在视频窗口该人的最后一张图像,直到有一个新进来的人。图5-9显示了4个来自四个不同国家的人在聊天应用程序上。这视图显示了本地用户在最上方的位置,不过,其他三人也看到自己在最上方的位置。

图5-9. Four-way conference in four countries

clip_image004

这个应用程序的目的,一直保持简单。您可以添加不同的增强,如使用video.clear()方法来清除萤幕,在旅客已离开了会议后。

5.7.继续前进到更多的服务端应用程序

现在,您已经看到如何在客户端与音频和视频工作(服务器端的一些小帮忙),下一步是在对服务器端与音频和视频工作。第6章:广播和服务器端的带宽控制 ,介绍了另一种类型的A/V应用-广播,更好地形容为一个到多个的应用。主要焦点,会介绍了FMS3服务器端的综合体。首先开始一些简单的服务器端使用。但正如您将很快地看到,一旦你开始使用服务器端脚本,您将有更多的选择和控制您建立的应用程序。

-_-||本章终于完了~~

(译)Learning Flash Media Server 3中文版-第5章之5/7

2008年6月4日 1 条评论

(翻译能力有限,仅供个人学习参考,看得懂就好-_-||)

Learning Flash Media Server 3中文版-第5章双向音频-视频通信

5.5.一个更好的双向聊天应用程序

虽然两个模块的聊天室应用程序是很容易就建立和良好的工作,其唯一目的是显示创建一个双向聊天需要最少的代码。更好的选择是一个双向的聊天室有一个单一的模块,并且可以告诉哪种方式来设定流。最佳方法是将其写入服务器端脚本。一个FMS3应用程序的概念是最好的赞赏,当你考虑一个脚本,所有用户使用相同的应用得到不同的信息。您熟悉变量在一个非FMS应用程序改变,但是使用非服务端数据,像数据库。但是,如果别人使用相同的应用,你这样做不会影响其他人使用相同的应用。例如线上游戏,没有服务器端组件。你的分数将不会影响玩同样游戏的其他人的分数。换个方式来考虑这个应用程序不是一个共享的。

在第3章, “非持久客户端远程共享对像”,你学习了共享对象和看到几个用户如何通过一个共同的应用连接,可能会影响他人。没有服务器端脚本应用在这些应用程序中,因为客户端脚本的沟通通过FMS而不需要一个脚本在服务器上。这一章向您介绍服务器端脚本追踪,不管一个或两个用户在一时间内都使用相同的应用程序。脚本与在前面讨论的两个模块的应用程序部分有很大的共通点,接下来这个应用程序只使用一个。因为由两个模块所做的工作现在由服务器端脚本来操作了,您只需要一个单一的模块。

5.5.1. Keeping Track of Users跟踪用户

在开始服务端脚本之前,你必须转变一下你的思想:不是使用ActionScript 3.0的,对Flash Media Server 3.0,基本上是您使用的ActionScript 1.0与少数的类。 (如果您不熟悉的ActionScript 1.0 ,把该脚本想像为一个略加修改的JavaScript ) 。这意味着数据没有类型(没有指定数据类型)和您可以使用prototype(原型)来创建类。

无论如何,这第一个脚本是只会做一件事。使用一个数组与两个要素,它将使用array.pop()方法来提供两个字符串中的一个,left和right。当客户端离开,使用array.push()来把元素放置回去 。了解这一切是如何工作的,您首先需要了解两个主要服务器端的类,Application和Client。

服务端ActionScript有大概-15个类。其中三个类一个可以处理XML和两个处理SOAP。实际上,你需要花大部分的时候在其它4个类上:Application, Client, SharedObject 和 Stream。在这个例子中,你只需要前面两个。

5.5.1.1. Application类

顾名思义,Application类处理整个应用程序。每个应用程序,不管有多少客户端连接,都有一个单一的应用程序对象。是什么使这个类不寻常的是,这是自动实例作为应用。所以,当你写SSAS代码,您不必创建一个对象,它自动为您创建。因此,使用这个类,您只需这样写:

application.doSomething...

这可能会混乱,想像这是少了一个步骤来实例化一个类的实例。

剩下的章节会探究服务端代码,将会进入到更多的服务端类中,同样也会涉及到Application类的更多的属性,方法,和事件,

Application 事件

· onAppStart

· onConnect

· onDisconect

Application 方法

· rejectConnection

· acceptConnection

客户端只要连接到该应用程序就可以触发应用程序事件。第一个客户端打开应用程序触发onAppStart事件。随后使用者(客户)将不会影响这一事件,只要在应用程序运行时-其中最后客户离开了应用程序 20分钟后,-事件并不触发。

每个连接连接到应用程序都会触发onConnect事件。每个新连接都是通过RTMP来连接的,这个连接可以用Application方法来接受或者拒绝,acceptConnection或者rejectConnection

每当一个客户端断开,客户从客户数组中移除。您也可以使用Application.onDisconnect事件来查找哪个客户断开了。这出现在服务器端脚本中。

5.5.1.2. Client类

像Application类,Client类也自动建立一个它的实例。每一个在应用程序中的客户,都变成应用程序的客户数组中的一部分,每个客户是一个数组元素。例如,假设Nancy, Pete and Juan都连接到相同的应用程序中,Nancy是第一个连接,Juan是最后一个连接。如下所示:

Nancy = application.clients[0];

Pete = application.clients[1];

Juan= application.clients[2];

注意Application的客户属性是带个s:clinets(您可以节省大量的调试时间,记住这一点。)像服务器端脚本显示,Client的实例名称是currentClient 。有点像下面这样:

currentClient = new Client();

然而,这并不是Client的实例名称如何生效。相反,客户端从它连接到服务器的事件函数得到其参考的名字。图5-4显示正确的命名程序。

图5-4. Naming Client instance

clip_image002

currentClient实例对所有客户都有用处,但是紧记,每个连接到应用程序的用户都是Application.clients数组的一部分。像所有类一样,这个实例也可以使用内置的属性,方法和事件。可是,你也可以建立你自已的属性和方法和其它任何类一样。在这章中的应用程序中,属性和方法是为application创建的。脚本中包含用户的方法和属性:

用户的方法写成:

currentClient.streamSelect = function();

这个方法是由客户端调用的。看看客户端脚本是怎么办调用的

添加到currentClient实例的属性叫cliNow,它指定为vidStreams数组的值

currentClient.cliNow=vidStreams.pop();

在脚本顶部中的数组,只有两个元素出现:”right”和”left”。 在streamSelect方法中返回给客户端currentClient.clinow属性的值。

5.5.2. The Server-Side Script服务端脚本

建立一个服务端脚本,产生一个只有两个参与的客户端名称和拒绝其它的客户端,跟随以下步骤:

1. 建立一个deux.asc

2. 输入以下代码

Example 5-2. deux.asc

Code View:

//Two-element array
vidStreams=["right","left"];
 
//应用程序第一次启动
application.onAppStart = function()
{
  trace("The deux is out of the deck");
};
 
//A currentClient (user) attempts to connect
application.onConnect = function(currentClient)
{
 //如果数组是空的话就拒绝连接
 //(当前有两个用户正在使用应用程序)
  if (vidStreams.length <= 0)
 {
  application.rejectConnection(currentClient);
 }
 
 //Store array element in currentClient property
  currentClient.cliNow=vidStreams.pop();
 
  application.acceptConnection(currentClient);
  currentClient.streamSelect = function()
  {
        trace("Stream "+currentClient.cliNow+" used");
  //Sent the property to Client object
        return currentClient.cliNow;
  };
};
application.onDisconnect = function(currentClient)
{
 //When currentClient leaves put the element back in array
 vidStreams.push(currentClient.cliNow);
}

3. 在Flash Media Server 3目录下的application目录下建立一个deux的目录

4. 保存deux.asc在deux目录下。或者你也可以把它重命名为main.asc,根据你的喜好。(我比较喜欢命名为应用程序的名称,如果我所有都命名为main.asc,一个不注意被覆盖或者移动,就非常难找了)

在开始写客户端脚本之前,你需要明白这个服务端脚本到底是在什么。无论何时,一个客户端连接到应用程序时,它从数组中删除一个元素,然后通过客户端脚本返回它给客户端。该脚本发送字符串,以确定流出的流和进来的流用什么名称。一旦这两个元素的数组是空的,它不会允许任何其他客户端连接。当一个客户端离开,它把元素放回数组中,然后现在不同的客户端可以加入聊天室了。如果双方当事人离开,两个更多的客户可以使用它。除其他事项外,此脚本保证一个私人的线上影音聊天室。(当然,除非你自已对谈自己) 。

5.5.3. The Client-Side Strategy客户端策略

一旦你完成了服务端脚本,现在你可以来设置客户端脚本了。你可以对前面制作的两个模块的应用程序进行简单的修改,把它转换为一个模块的聊天室

为了保证流声音和视频正确地输出,您只需要从服务器端脚本得到一个唯一的字符串。该服务器端脚本返回两个字符串中的一个,用来命名流的名称-left和right。AS3.0 的Responder类用来捕捉服务器端的信息,这样您可以使用它在客户端脚本中。

5.5.3.1. Responder类

Responder类有处理返回的数据工作的参数,同样也可以处理错误,这个应用程序焦点只在第一个参数上,所以这个例子没有使用处理错误(后面的例子中会使用到)。图5-5显示了呼叫服务端和回应者角色正在捕捉返回的数据:

Figure 5-5. Responder instance and call to server

clip_image004

在图5-5中,Responder实例指定了一个回调函数用来捕捉呼叫服务端所返回的。NetConnection.call() [nc.call("streamSelect",responder);] 方法包含了服务端的函数名称,前面的呼叫指向到了下面这行(这行在服务端脚本中):

currentClient.streamSelect = function();
5.5.3.2. Responder回调函数

一旦发出呼叫和指定回应函数,回调函数要求一个用来捕捉从服务端来的数据的参数。图5-6显示了回调函数是怎么样设置的和每个部分做什么。

图5-6. Responder instance and call to server

clip_image006

在5-6中,回调函数的参数是用来捕捉从服务端发出的东西的关键,因为它包含返回字符串的值。现在,这脚本使用这个值作为唯一的流名称。回调函数包含下面的switch语句:

switch (streamSelect)
{
  case "left" :
        outStream="left";
        inStream="right";
        break;
  case "right" :
        outStream="right";
        inStream="left";
        break;
}

Switch语句使用返回的值(在streamSelect中)来决定如何命名流。如果返回的值是”left”,流出的流就命名为”left”,进来的流就命名为”rigth”。反之亦然。

这个程序可能似乎相当复杂,只是在命名一对流名称上。但使用这个脚本请记住,,两个来自全球各地的用户可以成功地协调沟通。此外,这种协调可以安全的只使用一个单一的模块。

5.5.4.客户端应用程序

客户端文件包含一个与应用程序相同名称的logo。这个logo是一个影片,放置在库中,按以下步骤来建立fla文件和as文件:

1. 建立一个Deux.fla

2. 把logo放置在左上角(可选)

3. 在舞台中央建立一个文本内容为:Deux,坐标为:x=250, y=30.

4. 在文档类中输入Deux

5. 建立一个Deux.as与Deux.fla同一目录

6. 在Deux.as中输入以下代码:

Example 5-3. Deux.as

Code View:

package
{
  import flash.display.Sprite;
  import flash.display.MovieClip;
  import flash.events.NetStatusEvent;
  import flash.net.NetConnection;
  import flash.net.NetStream;
  import flash.media.Camera;
  import flash.media.Microphone;
  import flash.media.Video;
  import flash.net.Responder;
 
  public class Deux extends Sprite
  {
        private var nc:NetConnection;
        private var good:Boolean;
        private var netOut:NetStream;
        private var netIn:NetStream;
        private var cam:Camera;
        private var mic:Microphone;
        private var responder:Responder;
        private var vidOut:Video;
        private var vidIn:Video;
        private var outStream:String;
        private var inStream:String;
 
        public function Deux ()
        {
             var rtmpNow:String="rtmp://192.168.0.11/deux";
             nc=new NetConnection;
             nc.connect (rtmpNow);
             nc.addEventListener (NetStatusEvent.NET_STATUS,getStream);
        }
 
        private function getStream (e:NetStatusEvent):void
        {
             good=e.info.code == "NetConnection.Connect.Success";
             if (good)
             {
                   responder=new Responder(streamNow);
                   nc.call ("streamSelect",responder);
             }
        }
 
        private function streamNow (streamSelect:String):void
        {
             setCam ();
             setMic ();
             setVid ();
 
             switch (streamSelect)
             {
                   case "left" :
                        outStream="left";
                        inStream="right";
                        break;
                   case "right" :
                        outStream="right";
                        inStream="left";
                        break;
             }
 
             //Publish local video
             netOut=new NetStream(nc);
             netOut.attachAudio (mic);
             netOut.attachCamera (cam);
             vidOut.attachCamera (cam);
             netOut.publish (outStream, "live");
 
             //Play streamed video
             netIn=new NetStream(nc);
             vidIn.attachNetStream (netIn);
             netIn.play (inStream);
        }
 
        private function setCam ():void
        {
             cam=Camera.getCamera();
             cam.setMode (240,180,15);
             cam.setQuality (0,85);
        }
 
        private function setMic ():void
        {
             mic=Microphone.getMicrophone();
             mic.rate=11;
             mic.setSilenceLevel (12,2000);
        }
 
        private function setVid ():void
        {
             vidOut=new Video(240,180);
             addChild (vidOut);
             vidOut.x=25;
             vidOut.y=110;
 
             vidIn=new Video(240,180);
             addChild (vidIn);
             vidIn.x=vidOut.x+260;
             vidIn.y=110;
        }
  }
}

当你运行这个程序,你会看到比Easy应用程序更完美。不管如何,当前的两个用户会看到同样的东西。图5-7显示了当应用程序运行正确时,预期你可以看到的。

图5-7. Two-way chat with one module

clip_image008

通过使用一个单一的模块,您可以使用一个单一的文件名称。此应用程序可以与世界各地一起测试。即使它是一个简单的应用,它也可以用来与世界各地沟通。

(译)Learning Flash Media Server 3中文版-第5章之4/7

2008年6月3日 评论已被关闭
5.4.世界上最简单的双向的A/V聊天的应用程序

为了制作一个简单的双向A/V聊天应用程序,需要两个模块。紧记FMS3应用程序只是简单的表现在你使用的RTMP URL为简单的名称,你可以在一个应用程序中有多个模块。换句话说就是,应用的名称是在服务端的目录的名称。

总而言之,你将要开始制作这个应用程序。这个应用程序的名称将会很简单。你将会有两个模块,Easy1和Easy2。每个模块会通过存储在服务端的应用程序,连接到FMS3。对于这个应用程序,每个模块将会写成用来接受其它人发送的流。如你所见,一个简单的换来做所有真实的工作。所以任务真正地包括写一个模块,然后复制它,做一些修改。图5-2展示了两个视频将出现的区域,和它们的实例名:

图5-2. Video instances

clip_image002

开始之前,回顾一下需要的以下类和对像:

Classes

NetConnection

NetStream

NetStatusEvent

Camera

Microphone

Video

按以下步骤来制作这个聊天应用程序:

1. 建立一个Easy1.fla

2. 用文本工具添加一个文本,内容为Easy #1,x=205, y=223

3. 在文档类中输入Easy1

4. 建立一个Easy1.as与Easy1.fla同一目录

Example 5-1. Easy1.as

Code View:

package
{
  import flash.display.Sprite;
  import flash.events.NetStatusEvent;
  import flash.net.NetConnection;
  import flash.net.NetStream;
  import flash.media.Camera;
  import flash.media.Microphone;
  import flash.media.Video;
 
  public class Easy1 extends Sprite
  {
        private var nc:NetConnection;
        private var good:Boolean;
        private var rtmpNow:String;
        private var nsIn:NetStream;
        private var nsOut:NetStream;
        private var cam:Camera;
        private var mic:Microphone;
        private var vidLocal:Video;
        private var vidStream:Video;
 
 
        public function Easy1 ()
        {
             rtmpNow="rtmp://your.web.com/easy";
             nc=new NetConnection();
             nc.connect (rtmpNow);
             nc.addEventListener (NetStatusEvent.NET_STATUS,checkCon);
             setCam ();
             setMic ();
             setVideo ();
        }
 
        private function checkCon (e:NetStatusEvent):void
        {
             good=e.info.code == "NetConnection.Connect.Success";
             if (good)
             {
                   nsOut=new NetStream(nc);
                   nsOut.attachAudio (mic);
                   nsOut.attachCamera (cam);
                   nsOut.publish("left","live");
 
                   nsIn=new NetStream(nc);
                   nsIn.play("right");
                   vidStream.attachNetStream(nsIn);
             }
        }
 
        private function setCam()
        {
             cam=Camera.getCamera();
             cam.setKeyFrameInterval (9);
             cam.setMode (240,180,15);
             cam.setQuality (0,80);
        }
 
        private function setMic()
        {
             mic=Microphone.getMicrophone();
             mic.gain=85;
             mic.rate=11;
             mic.setSilenceLevel(15,2000);
        }
 
        private function setVideo()
        {
             vidLocal=new Video(cam.width,cam.height);
             addChild(vidLocal);
             vidLocal.x=15; vidLocal.y=30;
             vidLocal.attachCamera(cam);
 
             vidStream=new Video(cam.width,cam.height);
             addChild(vidStream);
             vidStream.x=(vidLocal.x+ cam.width +10); vidStream.y=vidLocal.y;
        }
  }
}

6. 确保rtmpNow="rtmp://your.web.com/easy"反射你自已的FMS URL,不管它是在你本机,LAN IP地址或者远程服务器的URL

7. 发布HTML和SWF

这已经完成了第一个模块,测试它,你会在左边窗口中看到你自已。下一步将简单的改变第一个模块。

8. Easy1.fla另存为Easy2.fla

9. 把文本改为Easy2

10.把Easy1.as另存为Easy2.as

11.改变类名称和构造函数的名称为Easy2:

public class Easy2 extends Sprite
....
public function Easy2()

12.在checkCon函数中,改变以下两行:

nsOut.publish("left","live");->nsOut.publish("right","live");
....
nsIn.play("right"); ->nsIn.play("left");

13.保存Easy2.as文件。现在你的两个模块应用程序已经完成。

步骤12显示了应用程序中的关键几行,Easy1发布一个叫left的流和播放一个叫right的流。Easy2刚好与Easy1相反,所以每个模块的用户可以播放另一个人的流。

测试这个应用程序,理论上你需要两个摄像头和两台电脑。一个用户运行Easy1.html,另一个用户运行Easy2.html。不管在LAN或者网上的远程服务器,两个模块都要能访问到FMS3服务器。图5-3中当你运行两个模块时你应该看到的。

图5-3. Two-way Audio-Video chat

clip_image004

如果可以在远端访问你的IP地址,那么你将可以与世界上的任何人聊天。