存档

文章标签 ‘Learning Flash Media Server 3中文版’

(译)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章之7/7

2008年11月25日 评论已被关闭

6.7. 总结

现在你已经开始写你自已的服务端代码了,你能够扩展你的FMS3应用程序的功能了。同你有能力建立你自已的服务端代码一样,你可以控制你的应用程序更多方面,从带宽方面着手,让它们有更多的交互或者只是改善它们的功能。  

新的Client.checkBandwidth 方法,而无需服务器端脚本,但作为服务器端脚本确实存在。在客户端的每一个方式,从调用功能,到获取信息回来,该checkBandwidth方法必须加工过,好像你可以看到它在服务器端脚本。想像它是在服务器端的一种无形的方法,因为这正是它以及它如何响应客户端的调用。

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

2008年11月25日 评论已被关闭

6.6. 带宽检测

在FMS3之前,检查你的系统当前的带宽,唯一的方法来是使用服务器端代码和算法。一个非常有趣的新功能是call服务器, checkBandwidth 。由于call是一个内置的功能,您就可以用来客户端代码检查自己的带宽。checkBandwidth命令的调用是在一个NetConnection对象中:

//nc is a NetConnection instance
nc.call ("checkBandwidth",null);

它看起来是call服务端的函数checkBandwidth ,但是在服务端你不需要与任何的代码。虽然你不写这个方法,但它仍然属于服务器端的Client类。你必须写一个特殊的类来得到返回的信息,用两个特殊的方法:onBWCheckonBWDone 。这个类是被NetConnection.client实例调用的。

6.6.1. 创建Client类

第一步是写一个客户类,包含用来来检查带宽所需要的方法和返回一个值。此外,通过包括getter方法,你可以为你的主类返回带宽。客户端类虽然可以作为你的主类; ,但是还是把它单独开,这样任何其它应用程序都可以很容易的重复使用,当需要获取带宽时。在开始前,建立BWClient.as文件,示例6-10 。这个类是用在NetConnection.client实例上的。

Example 6-10. BWClient.as

Code View:

package
{
    class BWClient
    {
          private var currentBW:Number;

          public function onBWCheck (... rest):Number
          {
               return 0;
          }
          public function onBWDone (... rest):void
          {
               if (rest.length>0)
               {
                     currentBW=rest[0];
               }
          }
          public function sendInfo ():Number
          {
               return currentBW;
          }
    }
}

...(rest)参数表明可以接受多个参数。第一个方法,onBWCheck 返回0。在NetConnection.call("checkBandwidth", null)时,它担任启动的角色。在这个类中的关键的方法onBWDone。rest[0]元素包含目前带宽的速度(千比特)。为了在主类中能能够做点事情, sendInfo ()方法是一个公共的方法,是主类可以用来取得当前带宽的唯一方法。 下一步, CheckBW类调用客户类(BWClient),提取带宽,并显示给该用户。当然,带宽可以用来做任何调整摄像头和声音的设置,使其他调整来优化FMS3应用程序。输入示例6-11中的代码,并保存为CheckBW.as 。

Example 6-11. CheckBW.as

Code View:

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

    public class CheckBW extends Sprite
    {
          private var nc:NetConnection;
          private var rtmpNow:String;
          private var good:Boolean;
          private var btn:Button;
          private var txtFld:TextField;
          private var checkUp:Object;
          private var currentBW:Number;

          public function CheckBW ()
          {
               btn=new Button();
               btn.x=200, btn.y=50;
               btn.label="Show Bandwidth";
               addChild (btn);
               btn.addEventListener (MouseEvent.CLICK,showBW);

               txtFld=new TextField();
               txtFld.multiline=true;
               txtFld.x=200, txtFld.y=80;
               txtFld.autoSize = TextFieldAutoSize.LEFT;
               addChild (txtFld);

               rtmpNow="rtmp://192.168.0.11/studio/";
               nc=new NetConnection ;
               nc.addEventListener (NetStatusEvent.NET_STATUS,checkNcall);
               nc.client=new BWClient();
               nc.connect (rtmpNow,"BWsnooper");
               checkUp=new Object();
          }
          private function checkNcall (e:NetStatusEvent):void
          {
               good=e.info.code == "NetConnection.Connect.Success";
               if (good)
               {
                     nc.call ("checkBandwidth",null);
               }
          }

          private function showBW (e:MouseEvent)
          {
               checkUp.see=nc.client.sendInfo();
               if (checkUp.see >0)
               {
                     currentBW=nc.client.sendInfo();
                     txtFld.text="Your current Bandwidth is "+currentBW +" Kbps.\n";
                     txtFld.appendText ("That would be about
"+Math.round(currentBW/1000)+ " Megabits per second.");
               }
               else
               {
                     txtFld.text="Still working.\n";
                     txtFld.appendText ("Click again in a couple of moments.");
               }
          }
    }
}

CheckBW 类在连接成功后就调用checkBandwidth 方法。为了看到你的带宽是多少,需要点击按纽,它会调用showBW 方法。使用BWClient实例(nc.client)来调用getter方法:sendInfo()。使用一个对像的属性(checkUp.see),储存返回的带宽值,然后传递给currentBW。依次,信息被传递给文本字段。即使在连接成功后就调用checkBandwidth 方法,也要等待一会再会返回信息给客户端。

Figure 6-12. Before and after bandwidth has been returned

image

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

2008年11月23日 评论已被关闭

6.5. 动态控制 Camera, Microphone, and Bandwidth

你可能很想知道,为什么在最初的广播应用程序中,对Camera和Microphone对像比较少评价。这只不过是把重点放在,用最少的代码创建一个广播型应用。这节中将添加几个组件到Broadcast 模块中。然而,添加这些组件,将消除”studio”的氛围,重点放在更多的不同的组件和他们所做的事情。你将能够看到在TV模块上的图像动态的改变,在你改变不同的组件时。同样地,你可以更好的明白到,所有的设置是多么的重要。竟然如此,你将使用完全相同的TV模块。因为这处应用程序使用不同的组件,主要是用户界面组件,你将会在类列表中看到,比前的例子更多的类和事件。像其它例子一样,你将使用StreamAV 类,所以确保StreamAV.as 文件和新的Broadcast 模块在相同的目录中。

Classes

NetConnection

NetStatusEvent

Camera

Microphone

Video

Sprite

TextField

StreamAV

DataProvider

MovieClip

List

TextInput

Button

Slider

SliderDirection

SliderEvent

RadioButton

RadioButtonGroup

TextField

Event

MouseEvent

Objects

Static Text fields

Drawn rectangle

backdrop_mc (instance)

你会看到,大部分的工作是在该文件BroadcastStudio.as中,动态分配不同的用户界面组件到舞台上。开始前,你需要把几个不同的静态文本放在舞台上,并创建一个影片剪辑对象和使用矩形绘图工具。为了帮助您,图6-9显示所有的对像,及其x和y坐标( X和Y )和两个绘制的对像的尺寸和位置。

Figure 6-9.对像和实例名称

image 

 

所有的静态文本,使用11号Arial Black字体(您可以代替任何您想要的字体)。请按照下列步骤来建立这个应用程序:

 

  1. 新建一个Flash file (ActionScript 3.0),保存它为BroadcastStudio.fla,和StreamAV.as同一目录。舞台尺寸600X500。
  2. 使用图6-9作为一个参考,添加不同的静态文本到舞台。
  3. 使用矩形工具,画一个200X150的矩形,圆角9,转为影片剪辑,实例名为backdrop_mc,其它参考图。
  4. 画那个75 x 21 的矩形,参数参考图
  5. 打开组件面板,添加以下组件到库面板中:Slider,TextInput, RadioButton, Button, and List。
  6. 在同一目录下新建一个BroadcastStudio.as
  7. 在BroadcastStudio.as 文件中输入以下代码。
Example 6-9. BroadcastStudio.as

Code View:

package
{
  import fl.data.DataProvider;
  import flash.display.MovieClip;
  import fl.controls.List;
  import fl.controls.TextInput;
  import fl.controls.Button;
  import fl.controls.Slider;
  import fl.controls.SliderDirection;
  import fl.events.SliderEvent;
  import fl.controls.RadioButton;
  import fl.controls.RadioButtonGroup;
  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;
  import flash.events.Event;
  import flash.events.MouseEvent;
 
  public class BroadcastStudio 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;
 
        //BW
        private var list:List;
        private var stuff:DataProvider;
        private var cli2sv:uint;
 
        //Camera
        private var qualBw:TextInput;
        private var qualQual:TextInput;
        private var modeW:TextInput;
        private var modeH:TextInput;
        private var modeFPS:TextInput;
        private var keyFrameI:TextInput;
        private var camBtn:Button;
 
        //Microphone
        private var rateGroup:RadioButtonGroup;
        private var r5:RadioButton;
        private var r8:RadioButton;
        private var r11:RadioButton;
        private var r22:RadioButton;
        private var r44:RadioButton;
        private var silLevelL:TextInput;
        private var silLevelT:TextInput;
        private var silL:uint;
        private var silT:uint;
        private var mGain:Slider;
        private var micBtn:Button;
 
        public function BroadcastStudio ()
        {
             //Set Camera and Microphone
             nc=new NetConnection ;
             nc.addEventListener (NetStatusEvent.NET_STATUS,checkConnect);
 
             //Camera default
             cam=Camera.getCamera();
             cam.setMode (192,144,15);
             cam.setQuality (0,85);
             //
             qualBw=new TextInput();
             addChild (qualBw);
             qualBw.x=20,qualBw.y=35;
             qualQual=new TextInput();
             addChild (qualQual);
             qualQual.x=(qualBw.x+qualBw.width+5),qualQual.y=35,qualQual.width=30;
             modeW=new TextInput();
             modeH=new TextInput();
             //
             modeFPS=new TextInput();
             addChild (modeW);
             modeW.width=30,modeW.x=20,modeW.y=95;
             addChild (modeH);
             modeH.width=30,modeH.x=(modeW.x+modeW.width+5),modeH.y=95;
             addChild (modeFPS);
             modeFPS.width=30,modeFPS.x=(modeH.x+modeH.width+5),modeFPS.y=95;
             keyFrameI=new TextInput();
             addChild (keyFrameI);
             keyFrameI.width=30,keyFrameI.x=20, keyFrameI.y=155;
             camBtn=new Button();
             camBtn.label="Set Camera";
             addChild (camBtn);
             camBtn.x=(keyFrameI.width+keyFrameI.x+5),camBtn.y=155,
             camBtn.addEventListener (MouseEvent.CLICK,setCam);
 
             //Microphone default
             mic=Microphone.getMicrophone();
             mic.rate=11;
             micBtn=new Button();
             micBtn.addEventListener (MouseEvent.CLICK,setMic);
             rateGroup=new RadioButtonGroup("Rate");
             r5=new RadioButton(),r8=new RadioButton();
             r11=new RadioButton(),r22=new RadioButton();
             r44=new RadioButton();
             r5.move(300,20),r8.move(300,40);
             r11.move(300,60),r22.move(340,20);
             r44.move (340,40);
             r5.label="5",r8.label="8",r11.label="11";
             r22.label="22",r44.label="44";
             r5.group=rateGroup,r8.group=rateGroup;
             r11.group=rateGroup, r22.group=rateGroup;
             r44.group=rateGroup;
             addChild(r5),addChild(r8);
             addChild(r11),addChild(r22);
             addChild (r44);
             rateGroup.addEventListener (MouseEvent.CLICK,setMic);
 
             silLevelL=new TextInput();
             silLevelL.move (296,104);
             silLevelL.width=30;
             addChild (silLevelL);
             silLevelT=new TextInput();
             silLevelT.move (331,104);
             silLevelT.width=60;
             addChild (silLevelT);
             micBtn=new Button();
             micBtn.move (296,130);
             micBtn.label="Set Silence Level";
             addChild (micBtn);
             micBtn.addEventListener (MouseEvent.CLICK,setSilence);
 
             mGain=new Slider();
             mGain.move (470,20);
             mGain.minimum=0;
             mGain.maximum=100;
             mGain.tickInterval=5;
             mGain.value=70;
             mGain.width=100;
             addChild (mGain);
             mGain.direction= SliderDirection.VERTICAL;
             mGain.addEventListener (SliderEvent.CHANGE,setGain);
 
             //Video default
             vid=new Video(cam.width,cam.height);
             vid.x=189;
             vid.y=228;
             addChild (vid);
 
             txtField=new TextField();
             txtField.x=190;
             txtField.y=205;
             addChild (txtField);
             doBW ();
             rtmpNow="rtmp://192.168.0.11 /studio";
             nc.connect (rtmpNow,"anchor");
        }
 
        //Cwonnection
        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");
             }
        }
        //Create List and Data Provider
        private function doBW ():void
        {
             list=new List();
             stuff=new DataProvider();
             stuff.addItem ({label:"Select BW"});
             stuff.addItem ({label:"Minimum", data:(10000/8)});
             stuff.addItem ({label:"Small", data:(20000/8)});
             stuff.addItem ({label:"Medium", data:(50000/8)});
             stuff.addItem ({label:"Large", data:(100000/8)});
             list.dataProvider=stuff;
             list.move (200,20);
             list.width=65;
             list.selectedIndex=0;
             addChild (list);
             list.addEventListener (Event.CHANGE,changeBW);
        }
        //Change the client bandwidth
        private function changeBW (e:Event):void
        {
             cli2sv=list.selectedItem.data;
             nc.call ("setBW",null,100,cli2sv);
             list.selectedIndex=0;
        }
        private function setCam (e:MouseEvent):void
        {
             cam.setQuality (Number(qualBw.text),Number(qualQual.text));
             cam.setMode
(Number(modeW.text),Number(modeH.text),Number(modeFPS.text));
             cam.setKeyFrameInterval (Number(keyFrameI.text));
             backdrop_mc.width=(cam.width+6);
             backdrop_mc.height=(cam.height+6);
             setVid ();
        }
        private function setMic (e:MouseEvent):void
        {
             mic.rate=Number(rateGroup.selection.label);
        }
        private function setSilence (e:MouseEvent):void
        {
             silL=Number(silLevelL.text);
             silT=Number(silLevelT.text);
             mic.setSilenceLevel (silL,silT);
        }
 
        private function setGain (e:SliderEvent):void
        {
             mic.gain=e.target.value;
        }
 
        private function setVid ():void
        {
             vid.width=cam.width;
             vid.height=cam.height;
        }
  }
}                                 

当你完成,可以测试代码。图6-10显示了你将看到不同的选项填补和改变屏幕的默认值:

Figure 6-10. Dynamic changes in BroadcastStudio module

image 

在这个控制面板,您有很多不同的选项。控制camera的只是一个单一的按钮,所以千万不要留下任何空白选项,当您按下Set Camera 按钮。当前的设置显示了,该模式质量可用带宽的范围。最好的图片是100 Kbits ;图像出现在Broadcast Studio 模块不仅反映了,在模式设置和视频窗口大小之间,不同的图像大小所产生之间的联系。然而,如果您设置的质量为10或100,本地是看不到改变的。而是在流发送到TV模块中。例如,图6-11显示了非常低的带宽设置在相机质量的带宽(只有20 Kbits ) ;如果你比较形象的工作室,在电视模块,你可以看到其中的差别。

Figure 6-11. Studio changes only show up in streamed view

image

 

直接连接您的相机到视频对象,会尽可能提供最好质量的图片。您需要使用BroadcastStudio模块和TV模块,才能真正看到所有的效果。例如,尝试优化质量,FPS,以及关键帧间隔(使用一个比较低值的间隔,如5或更少),然后设置带宽最低或small。您的图像就变得很呆板,意思就是很卡,不流畅!

此应用程序的目的是向你显示,你可以用多少低的带宽。举例来说,如果你预期会有更多的观众,你可能想改变TV模块的视频分辨率为160 × 120 (默认)或者少于200 × 150 ,在Quad application 中有做过(5.6.3 在第5章)。最重要的是,此应用程序演示了如何建立一个视频和音频,与许多不同的ActionScript 3.0用户界面组件,每个设置或者组件设置都影响着流应用程序。

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

2008年11月22日 评论已被关闭

6.4.1.使用SSAS(服务端脚本)

SSAS的书写与在动作面板或者脚本窗口中书写不一样。紧记,这个脚本在服务端运行,依赖于服务器中发生的事件。在开始之前,让我们一起看一下三个事件:

· 程序开始时

· 连接到程序时

· 从程序断开时

接受客户端是一个方法,不是一个事件。但是接受一个连接是一个连接进程的一部分。所以在这三个事件外,接受连接也包含在内。当你与服务端的脚本一起工作时,你也想要调试它。为了调试它,像客户端脚本一样,可以使用trace()语句。trace 信息不会出现在输出面板,你必须打开FMS3控制台,选择View Applications->Live Log。Live Log 窗口显示trace()的结果,像图6-5。无论如何,第一步你需要建立SSAS文件。

1. 打开一个新的ActionScript通信文件,保存它为studio.asc,存放在服务端studio目录

2. 在studio.asc文件中,添加6-5中的代码:

Example 6-5. studio.asc
application.onAppStart = function()
{//程序启动
  trace("The Broadcast has begun!");
};
application.onConnect = function(client,nameNow)
{//连接上
  client.name=nameNow;
  trace(client.name + " has connected");
  application.acceptConnection(client);
  trace(client.name + " has been accepted");
}
application.onDisconnect = function(client)
{//连接断开
  trace(client.name + " has left.");
}

脚本使用SSAS,报告在服务端发生了什么。这三个事件捕获包括:程序启动(onAppStart)、客户端连接(onConnect)、客户端断开(onDisconnect)。另外,Application.acceptConnection()允许客户端连接。如果使用Application.rejectConnection()方法,客户端将会被拒绝连接。

一旦你保存好服务端脚本,打开Broadcast模块。第一个trace结果将出现在FMS3 Administration console,如图6-5所示。当应用程序起动,第一个消息出现。在这个时候你看见应用程序的起动消息,因为,客户端可以进来和出去,而不需要重新启动应用程序。然后你将会看到一个叫”anchor”的客户端已连接上,然后是接受连接的消息。下一步,当你打开Viewer模块,控制台显示”viewer”连接上和接受连接的消息。最后,viewer离开,然后是anchor。到这里,studio应用程序显示0个clients连接上。

Figure 6-5. Trace statements in the Administration console
clip_image002

现在你已成功的建立了一个接受连接的服务端脚本。这只是让你明白,当一个客户端连接到一个服务器时会发生什么和服务器怎么样来辨认唯一的客户端。

6.4.2.设置服务端客户带宽限制

让我们来做一些实用的ASC脚本。因为在与视频流和声音流一起工作的时候,控制带宽是非常的重要。你将看到如何对不同的客户端设置不同的带宽限制。这个应用程序将会只有两个客户端:anchor和viewer,所以你可以根据客户端的名称来设置带宽。Broadcast模块只是发送流,Viewer模块只是接收流。

使用Client.setBandwidthLimit()方法,你可以指定不同的客户端可使用的,服务端到客户端(server-to-client)和客户端到服务端(client-to-server)带宽。设置的语句的格式如下:

client.setBandwidthLimit(s2c, c2s);

S2c是服务端到客户端,c2s是客户端到服务端。通过设置不同的值来作为参数,可以根据客户端的名字来赋适当的值。下面的脚本对两个模块设置不同的带宽限制。

Example 6-6. studio.asc

Code View:

application.onAppStart = function()
{
  trace("The Broadcast has begun!");
};
application.onConnect = function(client,nameNow)
{
  client.name=nameNow;
  trace(client.name + " has connected");
  application.acceptConnection(client);
  trace(client.name + " has been accepted");
  var s2c;
  var c2s;
  if(client.name == "anchor")
  {
        s2c=1000;
        c2s= 100000/8;
  }
  else
  {
        s2c=100000/8;
        c2s=1000;
  }
  client.setBandwidthLimit(s2c,c2s);
  trace(s2c);
  trace("server to client=" + client.getBandwidthLimit(1));
  trace(c2s);
  trace("client to server=" + client.getBandwidthLimit(0));
}
 
application.onDisconnect = function(client)
{
  trace(client.name + " has left.");
}

为了测试新的SSAS文件,你必须重新启动应用程序。你可以在FMS3控制台中选择View Applications然后点击Reload Application按纽(带箭头的圆)。clip_image004

图6-6显示了选择的应用程序和重新加载应用程序的按纽。这是移除旧版本的应用程序的缓存和加载新版本的文件。(这个步骤很容易被忽略,往往你的程序的改变没有生效,就是因为没有重新加载应用程序)

Figure 6-6. Reload applications before running new server-side scripts

clip_image006

重新加载应用程序后客户端的连接会被断开,所以你当你重新加载后,要重新运行anchor和viewer,输出如下:

anchor has connected

anchor has been accepted
1000
server to client=1000
12500
client to server=12500
viewer has connected
viewer has been accepted
12500
server to client=12500
1000
client to server=12500

现在,两个模块实例也有了不同的带宽限制。客户端到服务端的带宽在Broadcast模块中比较高,因为这个模块必须从客户端发送流到服务器。当你做完这样的调整,你可以优化在你应用程序中使用的带宽数量。总之就是哪个为主哪个多设点吧。

6.4.3. 从客户端运行一个服务端动作

到目前为止,所有在服务端脚本中使用的事件都是使用Application事件,从服务器中运行的。使用NetConnection.call()函数,你也可以从客户端运行一个服务端的方法,及传递变量到服务端脚本中。例如,动态改变带宽的值,你可以发送一个消息连同值来启动服务端函数和传递值来设置带宽的限制。当你不期望有返回值和发送数据时,通常格式是这样:

nc.call("ssFunction", null, arg1, arg2);

如果你只是想调用某个函数和不传任何值,你可以使用这种格式:

nc.call("ssFunction");

On the server-side script, you will need a function that “catches” the call from the client side. The client class on the server side is the “catcher” of function calls. The format is:

在服务端脚本中,你需要一个函数”catches”从客户端调用。Client 类在服务端是”catches”函数,格式如下:

client.funcName = function(arg) {
//code
}

For example:

client.setBW = function(ser2c, c2ser) {
client.setBandwidthLimit(ser2c, c2ser);
}

如果你正在运行broadcast studio,你需要一个简单地方法来改变带宽的限制,以至于你可以更简单地测试你的应用程序和你想要设置的限制。实际上你控制的是Broadcast Studio模块,所以你不需要担心TV模块。在你可以控制任何在服务端的东西前,你需要改变一下studio.asc 脚本,如下:

Example 6-7. studio.asc
application.onAppStart = function()
{
  trace("The Broadcast has begun!");
};
 
application.onConnect = function(client,nameNow)
{
  client.name=nameNow;
  trace(client.name + " has connected");
  application.acceptConnection(client);
  trace(client.name + " has been accepted");
  client.setBW=function(ser2c, c2ser)
  {
        client.setBandwidthLimit(ser2c, c2ser);
        trace("New server to client= " + (ser2c*8)+"bps");
        trace("New client to server= " + (c2ser*8)+"bps");
  }
}
 
application.onDisconnect = function(client)
{
  trace(client.name + " has left.");
}

服务端函数从客户端接收两个参数,ser2c和c2ser。这些值用来设置带宽限制。记住在FMS中是bytes,bytes被转换成bits。一个byte是8 bits,所以100 bytes是 800 bits。

为了让两端都能一起工作,你需要改变Broadcast.as和Broadcast.fla文件,跟着以下步骤,从旧的中来建立一个新的应用程序:

1. 打开Broadcast.fla 和 Broadcast.as,分别另存为BroadcastSetBW.fla 和 BroadcastSetBW.as

2. 选择BroadcastSetBW.fla,改变它的文档类为BroadcastSetBW。拖一个List组件到库中,保存文件

3. 选择BroadcastSetBW.as文件,输入Example 6-8中的代码。

Example 6-8. BroadcastSetBW.as

Code View:

package
{
    import fl.data.DataProvider;
    import fl.controls.List;
    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;
    import flash.events.Event;
 
    public class BroadcastSetBW 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;
          private var list:List;
          private var stuff:DataProvider;
          private var cli2sv:uint;
 
          public function BroadcastSetBW ()
          {
               //Set Camera and Microphone
               nc=new NetConnection ;
               nc.addEventListener (NetStatusEvent.NET_STATUS,checkConnect);
               cam=Camera.getCamera();
               cam.setMode (200,150,15);
               cam.setQuality (0,85);
               mic=Microphone.getMicrophone();
               mic.rate=11;
 
               vid=new Video(200,150);
               vid.x=150;
               vid.y=90;
               addChild (vid);
 
               txtField=new TextField();
               txtField.x=155;
               txtField.y=72;
               addChild (txtField);
 
               doList ();
 
               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");
               }
          }
 
          //Create List and Data Provider
          private function doList ():void
          {
               list=new List();
               stuff=new DataProvider();
               stuff.addItem ({label:"Select BW"});
               stuff.addItem ({label:"Minimum", data:(10000/8)});
               stuff.addItem ({label:"Small", data:(20000/8)});
               stuff.addItem ({label:"Medium", data:(50000/8)});
               stuff.addItem ({label:"Large", data:(100000/8)});
               list.dataProvider=stuff;
               list.move(355,90);
               list.width=65;
               list.selectedIndex=0;
               addChild (list);
               list.addEventListener(Event.CHANGE,changeBW);
          }
 
          private function changeBW(e:Event):void
          {
               cli2sv=list.selectedItem.data;
               nc.call("setBW",null,100,cli2sv);
               list.selectedIndex=0;
          }
    }
}

Thus, the only the server-side bandwidth setting that needs to be controlled is the client-to-server bandwidth limit. To see the code making calls to the server in action, the next section explains how to set up a totally dynamic studio, including the module for changing the bandwidth limits.

为了更好的看到客户端和服务端的关系,图6-7用箭头指出了相关联的参数。Trace语句显示了参数是否正确传递。因为Broadcast模块不接收任何流,服务端到客户端的设置是静态的。

Figure 6-7. Client-side call to server-side function

clip_image008

为了更好的看到通过List组件传递的值,表6-1展示了所有用到的值。

Table 6-1. List labels and data values

image

所有的值都是设置成bps(bits每秒),注意,因为带宽是以bits为标准的,不是字节。所以在这里,提供的比率最小是10 Kbps,最大是100 Kbps。图6-8展示了你可将在Broadcast模块中看到的:

Figure 6-8. Broadcast module showing List component for changing bandwidth limits

clip_image010

To see the effect that changing bandwidth limits has on the studio and TV modules, test the TV module with the new studio module. The default bandwidth setting works well. But as you keep decreasing bandwidth for the studio to use to send a stream to the TV module, you will experience freezing. As you increase the bandwidth limits, the freezing and latency disappears. Try experimenting with this module to get a feel for how the changes, especially the latency and freezes, affect the application.

(译)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的代码。