Red5入门教程

来源:互联网 发布:知乎专栏稿费 编辑:程序博客网 时间:2024/05/17 00:55

 

简介:

   flex,java盛行天下时red5把二者连为一体成为更强的强者,为即时通信,流媒体开发,

网络在线游戏提供了一个简单易行的方案。越来越多的公司把这个方案当成了首先,熟悉相

关技术的人才的缺少也就成了发展的障碍,而相关资料的奇缺成了技术人员成长的障碍。

  本书注重实用,着眼入门,用大量实际的例子力求带领您进入flex,red5结合开发的天堂。

没一个列子都是精心挑选,每一个列子都代表着一个知识点,每一个列子都将成为您以后项

目的一部分。当然这是入门级别的书籍,不可能把所有的相关知识都讲到,

java,flex,spring,mina等等等等都red5相关,如果要对每一个点讲解那将是一个系列,而不是

一本书。为了突出重点,本书只讲解把这些联系起来的纽扣red5,并讲解如何利用red5

发项目。

  内容包括red5的安装,red5开发中各种概念,各种问题的解决方案,开发工具的使用等

等,但有详有略,如工具的使用只讲解了书本中需要用到的相关知识。最后书本还有数个完

整的列子,也是实际开发中常用的列子,如聊天程序,播放视频程序。

 

目录:

一.Red5介绍及安装:

        Red5介绍

        安装red5

二.编写运行第一个程序

        编写服务器端

        编写客户端

        运行自己编写的程序

三.远程调用

        客户端调用服务器端方法

        Red5调用客户端方法

        遍历所有连接到服务器端的用户

        好友上线通知

四.sharedObject共享对象

        sharedObject介绍

        游戏中移动同步的原理和简单实现1

 

五.服务器执行计划

六.录制播放视频

        播放服务器端视频

        录制视频

        设置视频保存位置

七.利用scoperoom管理客户端

八.视频播放程序

九.简单聊天程序

附录:开发工具的使用

 

一.RReedd55介绍及安装

  1.Red5介绍

 

   Red5是一个采用Java开发开源免费的的Flash流媒体服务器,功能和AdobefmsFlashMedia

 

Server)相同,可以上官方网站http://www.osflash.org/red5免费下载并使用。它支持把音频(MP3

 

和视频(FLV)转换成播放流,在下载的同时播放流;录制客户端播放流(只支持FLV),把摄像头,麦

 

克风等传入的视频音频录制保存到服务器;共享对象;现场直播流发布;远程调用。Red5使用RSTP作为

 

流媒体传输协议。

 

   Red5的功能使其在许多领域得到应用。如视频播放网站,远程教育,视频会议程序,聊天程序,网络

 

游戏等等。

 

   免费开源使软件更加容易扩展,下载后你可以对源代码进行修改;更加经济,比起fms高昂的费用red5

 

能为一般的应用节约大笔费用;服务器端的java语言比起fms服务器端的actionscript2语言更加面向对

 

象更加成熟。这使得red5还没到版本1就已经红遍世界。

 

  2.red5 安装

 

  第一步上java.sun.com下载jdk6并安装。如果不是安装在默认路径请设置classpathpath。设

 

classpathpath的方法请参考其他java的资料。

 

  第二步上http://www.osflash.org/red5下载Red5v0.7.0Finalwindow安装版。点击下载到

 

exe文件即开始安装,安装过程中选择要把red5安装到的目录。安装结束后打开浏览器输入

 

http://localhost:5080/如果能打开显示“Red5TestPage”表示安装成功。安装成功后可以运行查看

 

red5自带的例子。

 

第三步 red5的启动和关闭。安装版的red5默认情况下会在计算机启动时启动,如果想关闭自启动可以

 

右击我的电脑->管理->服务和程序,找到red5的那项把启动方式的自动启动改为手动或其他的。更改了

 

启动方式后可以找到red5的安装目录下的wrapper目录下的Red5.bat文件,双击即可启动,这样可以

 

从窗口看到客户端连接的情况。关闭窗口即可关闭red5,也可以双击StopRed5-NT.bat关闭服务程序。

 

二.编写运行第一个程序

 

   第一个程序的运行是学习的莫大回报,也是继续下面学习的关键,只有顺利的运行了第一个程序才会

 

更加信心百倍的去学习更深一步的知识。

 

   Red5+flexred5+flash的项目一般都包括服务器和客户端俩个部分,服务器用java,客户端用

 

flexflash。编写并编译完服务器端代码后需要把服务器的的应用部署到red5webapps目录下这和

 

传统的java应用没区别,客户端可以直接点击flash文件运行。

 

   下面开始第一个程序的编写,它分成服务器和客户端俩部分,以后的程序也都安这种方式分。

 

   1.编写服务器端

 

   打开eclipse新建java工程或javaee工程chapter2(如果不熟悉eclipse请参考附件eclipse

 

使用)。把red5安装目录下的red5.jar以及lib目录下的jar都添加到工程的类库中。

 

新建包first,然后新建类,类名称为Application并继承ApplicationAdapter。类的代码如下:

 

packagefirst;

 

import org.red5.server.adapter.ApplicationAdapter;

 

import org.red5.server.api.IConnection;

 

publicclassApplicationextendsApplicationAdapter{

 

    publicbooleanappConnect(IConnectionconn,Object[]args){

 

         System.out.println("连接");

 

         returntrue;

 

     }

 

     publicStringchange(Stringstr){

 

         System.out.println("客户端调用服务器");

 

         returnstr.toUpperCase();//传入的字符串转换成大写后返回

 

     }

 

}

 

2.编写客户端

打开flexbuilder3新建flex工程(具体flexbuilder的使用请参考附录),名字取名为chapter2

打开项目中chapter2.mxmlchapter2.mxml的代码如下:

 

<?xmlversion="1.0"encoding="utf-8"?>

 

<mx:Applicationxmlns:mx="http://www.adobe.com/2006/mxml"layout="absolute"

 

fontSize="12"backgroundGradientAlphas="[1.0,1.0]"

 

backgroundGradientColors="[#FDF9F9,#FDF9F9]"width="442"height="344">

 

     <mx:Script>

 

         <![CDATA[

 

              import mx.controls.Alert;

 

              privatevarrtmpURL:String="rtmp://localhost/chapter2";

 

              privatevarconn:NetConnection=newNetConnection();

 

              privatevarisConnectSuccess:Boolean=false;

 

              privatevarresponder:Responder=newResponder(resultFun);

 

              privatefunctionresultFun(object:String):void{

 

                   trace(object);

 

                   result.text=object.toString();

 

              }

 

              privatefunctionclickConnect(e:MouseEvent):void{

 

                   conn.addEventListener(NetStatusEvent.NET_STATUS,netStatus);

 

                   conn.connect(rtmpURL);

 

              }

 

              privatefunctionclick(e:MouseEvent):void{

 

                   invoke();

 

              }

 

              privatefunctioninvoke():void{

 

                   if(isConnectSuccess){

 

                       conn.call("change",responder,str.text);//change是服务器端方法名称

 

                   }else{

 

                       Alert.show("还没连接到服务器");

 

                   }

 

              }

 

              privatefunctionnetStatus(e:NetStatusEvent):void{

 

                   trace(e.info.code);

 

                   if(e.info.code=="NetConnection.Connect.Success"){

 

                       isConnectSuccess=true;

                }

 

            }

 

        ]]>

 

    </mx:Script>

 

    <mx:Buttonx="224"y="175"label="调用服务器方法"click="this.click(event)"/>

 

    <mx:TextInputx="129"y="145"id="str"width="212"/>

 

    <mx:Labelx="129"y="119"text="显示从服务器端返回的字符"id="result"width="160"

 

fontSize="12"/>

 

    <mx:Buttonx="129"y="175"label="连接服务器"click="this.clickConnect(event)"/>

 

</mx:Application>

 

3.部署运行程序

   进入red5的安装目录下的webapps下,复制test文件夹,把复制的文件夹改名为chapter2

删除chapter2\WEB-INF目录下的文件夹。

把服务器项目下的classes目录下的文件夹first拷贝到chapter2\WEB-INF下。

red5-web.properties中的/test替换为/chapter2

red5-web.xml中的<beanid="test.service" class="org.red5.server.webapp.test.TestService"

singleton="true" lazy-init="default" autowire="default" dependency-check="default" /><bean

id="echo.service" class="org.red5.samples.services.EchoService" singleton="true" lazy-

init="default"autowire="default"dependency-check="default"/>删除。

org.red5.server.webapp.test.Application替换为first.Application

web.xml中的<param-value>/test</param-value>替换为<param-value>/chapter2</param-

value>

<taglib><taglib-uri>/tlds/c</taglib-uri><taglib-location>/WEB-INF/tlds/c.tld</taglib-location>

</taglib>删除

注意:webapps下的文件夹名chapter2,red5-web.properties中的chapter2web.xml中的chapter2

要保持相同,如果一个改了,其他的也改成相同的,所有以后的工程都这样。

重新启动red5

flexbuilder3中选择运行菜单中的运行项运行chapter2。在文本框里输入字符串,点击连

接按钮,然后点击调用方法按钮。上面如果显示出输入框里的字符串的大写表示运行正常。

  服务器端类有俩个方法,appConnect类从ApplicationAdapter继承来,当flash连接时触发,

 

可以返回truefalseTrue表示接受客户端的请求,false表示拒绝客户端的请求。如果拒绝那么以后

 

flash和服务器端的交互服务器都不会理会,所以当想拒绝某些ip的请求时只需有在用户连接时检查它的ip

 

是否在拒绝ip之内,如果在就返回false,那样就可以拒之门外了。change是自己定义的,客户可以直

 

接调用的一个远程方法。作用是把客户传入的参数变成大些后返回,此处目的在于运行一个red5flex

 

交互的过程,并没有检查传入的参数是否有大写或都已经是大写。

 

  客户端的代码稍多些,位于 <mx:Script><![CDATA[  ]]></mx:Script>之外的是图形界面的

 

代码,包括俩个按钮,一个输入框,一个显示文本,这就不详细讲解了,注意给按钮添加事件监听器。过

 

程是这样的,用户点击连接,按钮的监听器clickConnect响应,conn.connect(rtmpURL)连接到服务

 

器,在服务器和客户端建立一条信息交流的通道。当这条通道打通时netStatus执行,把是否是打通的开

 

isConnectSuccess设置为通的。当用户再点击调用按钮时,通过这条通道调用服务器端的方法

 

conn.call("change",responder,str.text)。传给服务器字符串str.text,当服务器端方法执行完后把返

 

回值给responder定义时的方法resultFun,这样一个客户端与服务器交互的过程完成。

 

三.远程调用

通过red5服务器端和客户端可以直接通过方法调用来交互,这是非常方便的(想想你在一

个类里面方法调用发方便性吧),服务器调可以用客户端的方法,客户端也可以调用服务器

端的方法。其实上面的例子就是通过方法调用实现的。

1.客户端调用服务器端方法

    通过red5可以从客户端直接调用服务器上java编写的方法。调用时指明被调用的方法

名称,如果有返回值则需要指明返回值处理的responder,如果被调用的方法有参数则需要

                                           "change"

                                           "change"

传入参数。可以引用第一章的例子,conn.call(""cchhaannggee"",responder,str.text);

                                                                public

                                                                public

表示客户端调用服务器端的名称为change的方法,查看javachange的定义ppuubblliicc String

change(String str){//函数体}发现参数是javaString 类型的;调用时传入的str.text

actionscriptString类型;假如change有多个入口参数,则可以把参数列在str.text后面,

并用'',"隔开。返回值由responder处理,change返回值是String类型的,要求responder定义

时的函数resultFun的入口参数是String类型。

    调用过程中需要注意的是参数类型的对应。具体可以看下列表

     Java               actionscript

 

     null               null

     int/float          Number

     double             Double

 

     boolean            Boolean

     String             String

 

     List               Array

     Map                Object

     Bean               Object

 

表说明假如java传给flash的参数是自定义Bean类型时,客户端接收到的是Object类型

    当被调用的方法没有返回值时把responder设置为null。具体怎么实现就不再雷述,请

看第二章的例子。

2.服务器调用客户端方法

不带返回的调用

  新建 eclipse工程 chapter3,并建立包 first,在包里新建一个类 Application并继承

ApplicationAdapterApplication到代码如下:

 

packagefirst;

 

import org.red5.server.adapter.ApplicationAdapter;

 

import org.red5.server.api.IConnection;

 

import org.red5.server.api.service.IServiceCapableConnection;

 

publicclassApplicationextendsApplicationAdapter{

 

    @Override

 

    publicbooleanappConnect(IConnectionarg0,Object[]arg1){

 

        //TODOAuto-generatedmethodstub

 

        callClient(arg0);

 

        returntrue;

 

    }

 

    privatevoidcallClient(IConnectionconn){

 

       if(conninstanceofIServiceCapableConnection){

 

           IServiceCapableConnectionsc=(IServiceCapableConnection)conn;

 

           sc.invoke("clientMethod",newObject[]{"One",1});

        }

 

    }

 

}

 

复制webapps下的chapter2文件夹,并改名为chapter3,把工程下class目录下的first拷贝覆盖以前

 

first,把red5-web.properties中的/chapter2,替换为/chapter3。把web.xml中的<param-

value>/chapter2</param-value>替换为<param-value>/chapter3</param-value>

重启red5

   新建chapter3客户端项目,chapter.mxml代码如下:

 

<?xmlversion="1.0"encoding="utf-8"?>

 

<mx:Applicationxmlns:mx="http://www.adobe.com/2006/mxml"layout="absolute"

 

fontSize="12"backgroundGradientAlphas="[1.0,1.0]"

 

backgroundGradientColors="[#FDF9F9,#FDF9F9]"width="442"height="344">

 

    <mx:Script>

 

         <![CDATA[

 

             import mx.controls.Alert;

 

             privatevarrtmpURL:String="rtmp://localhost/jiaocheng";

 

             privatevarconn:NetConnection=newNetConnection();

 

             privatevarclient1:Object=newObject();

 

             privatefunctionclickConnect(e:MouseEvent):void{

 

                 client1.clientMethod=this.clientMethod;

 

                 conn.client=this.client1;

 

                 conn.addEventListener(NetStatusEvent.NET_STATUS,netStatus);

 

                 conn.connect(rtmpURL);

 

             }

 

             privatefunctionnetStatus(e:NetStatusEvent):void{

 

             }

 

             privatefunctionclientMethod(str:String,num:Number):void{

 

                  Alert.show("接收"+str+(num+1));

 

             }

 

         ]]>

 

    </mx:Script>

 

    <mx:Buttonx="129"y="175"label="连接服务器"click="this.clickConnect(event)"/>

 

</mx:Application>

 

点击调试程序运行客户端,然后点击连接按钮可以发现输出“接收One2”表示服务器调用

客户端到方法成功。

  服务器端还是只有俩个方法,当客户端连接时服务器调用appConnect,在appConnect

 

调用callClient。在callClient中服务器调用客户端的方法clientMethod,并传给客户端俩个参数,一个

 

字符类型,一个数字类型。只有IServiceCapableConnection类型的连接才可以调用客户端方法,所以

 

要先对连接类型进行强制转换。

 

  再看客户端,有三个方法,因为不需要处理连接状态的变化事件所以用个空方法以便简单突出重点。用

 

户点击按钮连接,调用clickConnect方法连接服务器。还有一个方法给服务器调用的,这个方法附着在

 

conn.client上。因为ActionScript3NetConnection是封闭的,无法直接把方法附着在其上,但他

 

的一个属性clientObject类型的,是动态的。

 

服务器不但可以调用客户端的方法,还可以把客户端方法处理的值返回给服务器。

理服务器调用客户端方法有返回值就就必须实现IPendingServiceCallback接口,并调用方式

改为 sc.invoke("clientMethod", new Object[]{"One", 1},this);this是任意一个实现

IPendingServiceCallback接口的类,在接口的resultReceived方法中处理返回值。现在把上面

的类改成下面样子

 

packagefirst;

 

import org.red5.server.adapter.ApplicationAdapter;

 

import org.red5.server.api.IConnection;

 

import org.red5.server.api.service.IPendingServiceCall;

 

import org.red5.server.api.service.IPendingServiceCallback;

 

import org.red5.server.api.service.IServiceCapableConnection;

 

publicclassApplicationextendsApplicationAdapterimplementsIPendingServiceCallback{

 

    @Override

 

    publicbooleanappConnect(IConnectionarg0,Object[]arg1){

 

         callClient(arg0);

 

         returntrue;

 

    }

 

    privatevoidcallClient(IConnectionconn){

 

        if(conninstanceofIServiceCapableConnection){

 

            IServiceCapableConnectionsc=(IServiceCapableConnection)conn;

 

            sc.invoke("clientMethod",newObject[]{"One",1},this);

 

        }

 

    }

 

    publicvoidresultReceived(IPendingServiceCallarg0){

 

         System.out.println("来自客户端到返回:"+arg0.getResult());

 

    }

 

}

 

把客户端的

 

             privatefunctionclientMethod(str:String,num:Number):void{

 

                  Alert.show("接收"+str+(num+1));

 

             }

 

改成

 

             privatefunctionclientMethod(str:String,num:Number):String{

 

                  Alert.show("接收"+str+(num+1));

 

                  Return"客户端返回来的字符串";

 

             }

 

测试可以从red5的框里看到输出“客户端返回来的字符串"

 

3.遍历所有连接到服务器端的用户

 

    遍历所有链接到服务器的客户端是常常的事,如好友上线,则要及时把这个消息通知给

在线的人员。

    Red5通过IConnection.getscope()得到所在scope,通过scope可以得到连接到这个scope

的所有客户的连接。在得到连接后就可以通过连接调用客户端方法。如调用每个客户端的

clientMethod方法一遍可以把服务器改成下面的样子

 

packagechapter2;

 

import java.util.Iterator;

import org.red5.server.adapter.ApplicationAdapter;

 

import org.red5.server.api.IConnection;

 

import org.red5.server.api.IScope;

 

import org.red5.server.api.service.IPendingServiceCall;

 

import org.red5.server.api.service.IPendingServiceCallback;

 

import org.red5.server.api.service.IServiceCapableConnection;

 

publicclassApplicationextendsApplicationAdapterimplementsIPendingServiceCallback{

 

    @Override

 

    publicbooleanappConnect(IConnectionarg0,Object[]arg1){

 

         this.callEvery(arg0.getScope());

 

         returntrue;

 

    }

 

    privatevoidcallEvery(IScopescope){

 

         Iterator<IConnection>it=scope.getConnections();

 

         while(it.hasNext()){

 

             this.callClient(it.next());

 

         }

 

    }

 

    privatevoidcallClient(IConnectionconn){

 

        if(conninstanceofIServiceCapableConnection){

 

            IServiceCapableConnectionsc=(IServiceCapableConnection)conn;

 

            sc.invoke("clientMethod",newObject[]{conn.getSessionId(),1},this);

 

        }

 

    }

 

    publicvoidresultReceived(IPendingServiceCallarg0){

 

         System.out.println("来自客户端到返回:"+arg0.getResult());

 

    }

 

}

 

把服务器端的class覆盖以前的文件,运行客户端可以看到有一个新的客户连接时每个在线的客户端会弹

 

出新上线用户的用户id

 

    shareObject

    shareObject

四.sshhaarreeOObbjjeecctt共享对象

 

    共享对象

 

    游戏中同步的原理

 

    同步的简单实现

 

    共享对象分为本地和远程俩种,本地的可以看adobe官方文档,很详细,这里就讲解远程的。远程共

 

享对象是供指定客户端群共有的数据。就如你家的钱包是你和你爱人共有的一样(某些特殊除外),可以随

 

便花,一个人少了,代表另一个也少了,但是也有限制,不是每个人都可以花(我就不能花你家钱包的钱),

 

只有指定有权限的人才可以。要花钱先得获得钱包,然后还要打开钱包。共享对象为数据共享提供了很大

 

的方便,因为数据改变时会在每个客户端同步而不用人去干预太多。

 

    下面以一个移动同步的例子讲解,效果是移动方块,所有其他浏览器中的的方块都同时改变位置。

 

新建服务器项目chapter4,并新建类,代码如下

 

packagefirst;

 

import org.red5.server.adapter.ApplicationAdapter;

import org.red5.server.api.IConnection;

 

publicclassApplicationextendsApplicationAdapter{

 

     @Override

 

     publicbooleanappConnect(IConnectionarg0,Object[]arg1){

 

         returntrue;

 

     }

 

}

 

    修改把red5-web.xmlweb.xml,red5-web.properties中的chapter3替换为chapter4重启red5.

 

    新建flex应用代码如下

 

<?xmlversion="1.0"encoding="utf-8"?>

 

<mx:Applicationxmlns:mx="http://www.adobe.com/2006/mxml"layout="absolute">

 

     <mx:Canvasx="279"y="99"width="105"height="73"id="rect"

 

backgroundColor="#DA4040"

 

         mouseDown="this.mouseDown(event)"mouseUp="this.mouseUp(event)"

 

mouseMove="this.mouseMove(event)">

 

     </mx:Canvas>

 

         <mx:Script>

 

         <![CDATA[

 

              import mx.controls.Alert;

 

              privatevarrtmpURL:String="rtmp://localhost/jiaocheng";

 

              privatevarconn:NetConnection=newNetConnection();

 

              privatevarisConnectSuccess:Boolean=false;

 

              privatevarso:SharedObject;

 

              privatevarisDown:Boolean=false;

 

              privatefunctionclickConnect(e:MouseEvent):void{

 

                   if(!isConnectSuccess){

 

                      conn.addEventListener(NetStatusEvent.NET_STATUS,netStatus);

 

                      conn.connect(rtmpURL);

 

                   }

 

              }

 

              privatefunctionmouseDown(e:MouseEvent):void{

 

                   rect.startDrag();

 

                   isDown=true;

 

              }

 

              privatefunctionmouseUp(e:MouseEvent):void{

 

                   rect.stopDrag();

 

                   isDown=false;

 

              }

 

              privatefunctionmouseMove(e:MouseEvent):void{

 

                   if(isDown){

 

                        so.setProperty("x",rect.x);

 

                        so.setProperty("y",rect.y);

 

                   }

              }

 

              privatefunctionnetStatus(e:NetStatusEvent):void{

 

                   trace(e.info.code);

 

                   if(e.info.code=="NetConnection.Connect.Success"){

 

                        isConnectSuccess=true;

 

                   }

 

                   so=SharedObject.getRemote("point",conn.uri,true);

 

                   so.connect(conn);

 

                   so.addEventListener(SyncEvent.SYNC,syncro);

 

              }

 

              privatefunctionsyncro(e:SyncEvent):void{

 

                   rect.x=so.data.x;

 

                   rect.y=so.data.y;

 

              }

 

          ]]>

 

     </mx:Script>

 

          <mx:Buttonx="10"y="10"label="Button"click="clickConnect(event)"/>

 

</mx:Application>

 

运行俩个flash客户端,在一个里面拖动框,看另一个框是否同步改变位置,如果是则正常。

 

shareObject应用时客户端要先用SharedObject.getRemote("point",conn.uri,true)获取服务器端

 

的一个共享对象;然后用so.connect(conn)连接对象来同步;最后通过so.addEventListener(

 

SyncEvent.SYNC,syncro)可以响应共享对象的变化。服务器只响应客户端的连接,没有任何其他的代码。

 

如果要在服务器端处理共享对象则通过服务器端的ISharedObject接口实现。为了演示服务器端对共享

 

对象的控制把服务器端的代码改成如下

 

packagechapter4;

 

import org.red5.server.adapter.ApplicationAdapter;

 

import org.red5.server.api.IConnection;

 

import org.red5.server.api.IScope;

 

import org.red5.server.api.so.ISharedObject;

 

publicclassApplicationextendsApplicationAdapter{

 

     @Override

 

     publicbooleanappConnect(IConnectionarg0,Object[]arg1){

 

          returntrue;

 

     }

 

     @Override

 

     publicbooleanappStart(IScopearg0){

 

          System.out.println("appStart程序启动");

 

          this.createSharedObject(arg0,"point",true);

 

          ISharedObjectso=this.getSharedObject(arg0,"point");

 

          if(so!=null)

 

              so.addSharedObjectListener(newShareObjectListener());

 

          else

 

              System.out.println("point null");

 

          returntrue;

 

                      }

 

}

 

并添加类

 

packagechapter4;

 

import java.util.List;

 

import java.util.Map;

 

import org.red5.server.api.IAttributeStore;

 

import org.red5.server.api.so.ISharedObjectBase;

 

import org.red5.server.api.so.ISharedObjectListener;

 

publicclassShareObjectListenerimplementsISharedObjectListener{

 

     publicvoidonSharedObjectClear(ISharedObjectBasearg0){

 

         System.out.println("onSharedObjectClear");

 

     }

 

     publicvoidonSharedObjectConnect(ISharedObjectBasearg0){

 

         System.out.println("onSharedObjectConnect");

 

     }

 

     publicvoidonSharedObjectDelete(ISharedObjectBasearg0,Stringarg1){

 

         System.out.println("onSharedObjectDelete");

 

     }

 

     publicvoidonSharedObjectDisconnect(ISharedObjectBasearg0){

 

         System.out.println("onSharedObjectDisconnect");

 

     }

 

     publicvoidonSharedObjectSend(ISharedObjectBasearg0,Stringarg1,

 

              Listarg2){

 

         System.out.println("onSharedObjectSend");

 

     }

 

     publicvoidonSharedObjectUpdate(ISharedObjectBasearg0,

 

              IAttributeStorearg1){

 

         System.out.println("onSharedObjectUpdate");

 

     }

 

     publicvoidonSharedObjectUpdate(ISharedObjectBasearg0,

 

              Map<String,Object>arg1){

 

         System.out.println("onSharedObjectUpdate");

 

     }

 

     publicvoidonSharedObjectUpdate(ISharedObjectBasearg0,Stringkey,

 

              Objectvalue){

 

         System.out.println("更新共享对象的值             "+key+":"+value);

 

     }

 

}

 

把编译好的文件放到red5的应用下,重启red5。再允许客户端则每次移动都可以看到移动到的位置

 

五.服务器自动执行计划

 

可以做服务器端添加脚本来定时执行一些语句,如调用客户端的某个函数或者清空某些共享对象。

 

新建服务器包chapter5,新建类Application,代码如下:

 

packagechapter5;

import java.util.Iterator;

 

import org.red5.server.adapter.ApplicationAdapter;

 

import org.red5.server.api.IConnection;

 

import org.red5.server.api.IScope;

 

import org.red5.server.api.scheduling.IScheduledJob;

 

import org.red5.server.api.scheduling.ISchedulingService;

 

import org.red5.server.api.service.IServiceCapableConnection;

 

publicclassApplicationextendsApplicationAdapterimplementsIScheduledJob{

 

     StringjobID;

 

     @Override

 

     publicbooleanappConnect(IConnectionarg0,Object[]arg1){

 

          returntrue;

 

     }

 

     @Override

 

     publicbooleanappStart(IScopearg0){

 

          jobID=this.addScheduledJob(60000,this);

 

          returntrue;

 

     }

 

     privatevoidcallEvery(IScopescope){

 

          Iterator<IConnection>it=scope.getConnections();

 

          while(it.hasNext()){

 

               this.callClient(it.next());

 

          }

 

     }

 

     privatevoidcallClient(IConnectionconn){

 

         if(conninstanceofIServiceCapableConnection){

 

             IServiceCapableConnectionsc=(IServiceCapableConnection)conn;

 

             sc.invoke("clientMethod",newObject[]{conn.getSessionId(),1});

 

         }

 

     }

 

     publicvoidexecute(ISchedulingServicearg0)

 

               throwsCloneNotSupportedException{

 

          System.out.println("运行计划");

 

          this.callEvery(this.getScope());

 

     }

 

}

 

把编译后的文件放到red5下。重启red5

 

新建flex应用,代码如下:

 

<?xmlversion="1.0"encoding="utf-8"?>

 

<mx:Applicationxmlns:mx="http://www.adobe.com/2006/mxml"layout="absolute">

 

          <mx:Script>

 

          <![CDATA[

 

               import mx.controls.Alert;

 

               privatevarrtmpURL:String="rtmp://localhost/jiaocheng";

              privatevarconn:NetConnection=newNetConnection();

 

              privatevarisConnectSuccess:Boolean=false;

 

              privatevarso:SharedObject;

 

              privatevarclient1:Object=newObject();

 

              privatefunctionclickConnect(e:MouseEvent):void{

 

                  client1.clientMethod=this.clientMethod;

 

                  conn.client=this.client1;

 

                  if(!isConnectSuccess){

 

                     conn.addEventListener(NetStatusEvent.NET_STATUS,netStatus);

 

                     conn.connect(rtmpURL);

 

                  }

 

              }

 

              privatefunctionnetStatus(e:NetStatusEvent):void{

 

                  trace(e.info.code);

 

                  if(e.info.code=="NetConnection.Connect.Success"){

 

                       isConnectSuccess=true;

 

                  }

 

              }

 

              privatefunctionclientMethod(str:String,num:Number):void{

 

                  Alert.show("您还未注册");

 

              }

 

         ]]>

 

     </mx:Script>

 

         <mx:Buttonx="110"y="125"label="Button"click="this.clickConnect(event)"/>

 

</mx:Application>

 

运行客户端,点击连接,则每隔一段时间会弹出窗口提示没有注册。

 

六.录制播放视频

     1.播放服务器端视频

     新建服务器包chapter6,并新建类Application,代码如下:

 

packagechapter6;

 

import org.red5.server.adapter.ApplicationAdapter;

 

import org.red5.server.api.IConnection;

 

publicclassApplicationextendsApplicationAdapter{

 

     publicbooleanappConnect(IConnectionarg0,Object[]arg1){

 

         returntrue;

 

     }

 

}

 

     把编译后的文件放到red5下,重启red5

编写客户端,新建flex应用,代码如下

 

<?xmlversion="1.0"encoding="utf-8"?>

 

<mx:Applicationxmlns:mx="http://www.adobe.com/2006/mxml"layout="absolute">

 

<mx:UIComponentid="ui">

 

</mx:UIComponent>

 

         <mx:Script>

         <![CDATA[

 

              import mx.controls.Alert;

 

              privatevarrtmpURL:String="rtmp://localhost/jiaocheng";

 

              privatevarconn:NetConnection=newNetConnection();

 

              privatevarisConnectSuccess:Boolean=false;

 

              privatevarnetStream:NetStream;

 

              privatevarvideo:Video=newVideo();

 

              privatefunctionclickConnect(e:MouseEvent):void{

 

                   if(!isConnectSuccess){

 

                      conn.addEventListener(NetStatusEvent.NET_STATUS,netStatus);

 

                      conn.connect(rtmpURL);

 

                   }

 

              }

 

              privatefunctionnetStatus(e:NetStatusEvent):void{

 

                   trace(e.info.code);

 

                   if(e.info.code=="NetConnection.Connect.Success"){

 

                       isConnectSuccess=true;

 

                       netStream=newNetStream(conn)

 

                       netStream.client=newStreamClient();

 

                       video.attachNetStream(netStream);

 

                       netStream.play("a.flv");

 

                       ui.addChild(video);

 

                   }

 

              }

 

         ]]>

 

     </mx:Script>

 

         <mx:Buttonx="378"y="369"label="Button"click="this.clickConnect(event)"/>

 

</mx:Application>

 

另外新建类StreamClient代码如下

 

package

 

{

 

     publicclassStreamClient

 

     {

 

         public functionStreamClient()

 

         {

 

         }

 

         public functiononMetaData(info:Object):void{

 

              for(varn:*ininfo){

 

                   trace(n+":"+info[n]);

 

              }

 

         }

 

         public functiononPlayStatus(info:Object):void{

 

              for(varn:*ininfo){

                   trace(n+":"+info[n]);

 

              }

 

         }

 

     }

 

}

 

把一个a.flv的视频文件放到\webapps\jiaocheng\streams下,运行客户端既可以看到播放视频

 

     2.录制视频

新建客户端flex应用,代码如下:

 

<?xmlversion="1.0"encoding="utf-8"?>

 

<mx:Applicationxmlns:mx="http://www.adobe.com/2006/mxml"layout="absolute">

 

<mx:UIComponentid="ui">

 

</mx:UIComponent>

 

         <mx:Script>

 

         <![CDATA[

 

              import mx.controls.Alert;

 

              privatevarrtmpURL:String="rtmp://localhost/jiaocheng";

 

              privatevarconn:NetConnection=newNetConnection();

 

              privatevarisConnectSuccess:Boolean=false;

 

              privatevarnetStream:NetStream;

 

              privatevarvideo:Video=newVideo();

 

              privatefunctionclickConnect(e:MouseEvent):void{

 

                   if(!isConnectSuccess){

 

                      conn.addEventListener(NetStatusEvent.NET_STATUS,netStatus);

 

                      conn.connect(rtmpURL);

 

                   }

 

              }

 

              privatefunctionclickVideo(e:MouseEvent):void{

 

                   video.attachCamera(Camera.getCamera());

 

                   ui.addChild(video);

 

              }

 

              privatefunctionnetStatus(e:NetStatusEvent):void{

 

                   trace(e.info.code);

 

                   if(e.info.code=="NetConnection.Connect.Success"){

 

                       isConnectSuccess=true;

 

                       netStream=newNetStream(conn)

 

                       netStream.client=newStreamClient();

 

                       netStream.attachAudio(Microphone.getMicrophone());

 

                       netStream.attachCamera(Camera.getCamera());

 

                       netStream.publish("b.flv","record");

 

                   }

 

              }

 

         ]]>

     </mx:Script>

 

     <mx:Buttonx="378"y="369"label="录制"click="this.clickConnect(event)"

 

fontSize="16"/>

 

     <mx:Buttonx="310"y="369"label="视频"fontSize="16"

 

click="this.clickVideo(event)"/>

 

</mx:Application>

 

运行客户端,点击视频可以看到自己的视频,点击录制可以把视频保存到服务器上。如果没

有视频只有麦克风则点击视频看不到效果,但还是可以录制视频,只是是声音而已。录制的

视频保存在\webapps\jiaocheng\streams下。

     3.设置视频保存位置

 

录制的视频往往比较多,与red5放同一个目录有点不太合适,那么该怎么把录制的视频,播放的视频目录

 

放到其他的文件夹或分区呢?Red5要求实现IStreamFilenameGenerator接口,并在接口里面设置位

 

置。

 

新建服务器类,代码如下:

 

packagechapter6;

 

import org.red5.server.api.IScope;

 

import org.red5.server.api.stream.IStreamFilenameGenerator;

 

publicclassPathBeanimplementsIStreamFilenameGenerator{

 

     publicStringrecordPath="streams/";

 

     public StringplaybackPath="streams/";

 

     publicStringgenerateFilename(IScopescope,Stringname,GenerationTypetype){

 

         returnthis.generateFilename(scope,name,null, type);

 

     }

 

     publicStringgenerateFilename(IScopescope,Stringname,

 

            Stringextension,GenerationTypetype){

 

        Stringfilename;

 

        if(type==GenerationType.RECORD)

 

            filename =recordPath+name;

 

        else

 

            filename =playbackPath+name;

 

        if(extension!=null)

 

            filename +=extension;

 

        returnfilename;

 

     }

 

     publicStringgetRecordPath(){

 

         returnrecordPath;

 

     }

 

     publicvoidsetRecordPath(StringrecordPath){

 

         this.recordPath=recordPath;

 

     }

 

     publicStringgetPlaybackPath(){

 

         returnplaybackPath;

 

     }

 

     publicvoidsetPlaybackPath(StringplaybackPath){

         this.playbackPath=playbackPath;

 

    }

 

    publicbooleanresolvesToAbsolutePath(){

 

         //TODOAuto-generatedmethodstub

 

         returntrue;

 

    }

 

}

 

red5-web.xml中添加下面bean

 

        <beanid="streamFilenameGenerator"

 

         class="chapter6.PathBean">

 

        <propertyname="recordPath"value="E:\streams\"/>

 

        <propertyname="playbackPath"value="E:\streams\"/>

 

        </bean>

 

其中E:\streams\可以改成你计算机中的任意文件夹。把编译后的文件放到red5下,运行red5重启,运

 

行客户端应用Record。点击录制,即可在指定的文件目录下看到录制的视频。同样也可以播放指定目录

 

下的视频文件。

 

    Scope room

    Scope room

七.SSccooppeerroooomm讲解

 

   Red5有范围之分,就如jsp中的范围概念。Red5中的范围分成application,room,place,lobby

 

但是他们都是一样的东西scopescope嵌套,最上的称为Application,也是默认连接到应用时的范围,

 

连接到Applicationurlrtmp://localhost/appname/Application里面的子范围称为room

 

接的urlrtmp://localhost/appname/roomnum/。当然要连接到子范围必然会先连接到父范围。

 

在每个范围中保存着自己的连接的信息,sharedobject等。下面以一个列子说明scope的应用。这个列

 

子很简单,客户端连接到各种范围,服务器在客户端连接时把自己范围内的客户端id打印出来。

 

服务器端代码:

 

packagechapter7;

 

import java.util.Set;

 

import org.red5.server.adapter.ApplicationAdapter;

 

import org.red5.server.api.IClient;

 

import org.red5.server.api.IConnection;

 

import org.red5.server.api.IScope;

 

publicclassApplicationextendsApplicationAdapter{

 

    @Override

 

    publicbooleanappStart(IScopearg0){

 

         System.out.println("启动appStart");

 

         returntrue;

 

    }

 

    @Override

 

    publicbooleanappConnect(IConnectionarg2,Object[]arg1){

 

         IScopearg0=arg2.getScope();

 

         System.out.println("连接到 "+arg0.getName()+"ID列表: ");

 

         Set<IClient>i=arg0.getClients();

 

         for(IClientc:i){

 

             System.out.println(c.getId());

 

         }

         returntrue;

 

     }

 

     @Override

 

     publicbooleanroomStart(IScopearg0){

 

         System.out.println("启动roomStart");

 

         returntrue;

 

     }

 

     @Override

 

     publicbooleanroomConnect(IConnectionarg2,Object[]arg1){

 

         IScopearg0=arg2.getScope();

 

         System.out.println("连接到 "+arg0.getName()+"ID列表: ");

 

         Set<IClient>i=arg0.getClients();

 

         for(IClientc:i){

 

              System.out.println(c.getId());

 

         }

 

         returntrue;

 

     }

 

}

 

客户端代码:

 

<?xmlversion="1.0"encoding="utf-8"?>

 

<mx:Applicationxmlns:mx="http://www.adobe.com/2006/mxml"layout="absolute"

 

fontSize="16">

 

         <mx:Script>

 

         <![CDATA[

 

              import mx.controls.Alert;

 

              privatevarrtmpURL:String="rtmp://localhost/jiaocheng/";

 

              privatevarisConnectSuccess:Boolean=false;

 

              privatefunctionclickConnect(e:MouseEvent):void{

 

                   Alert.show("链接到:"+e.target.label);

 

                   rtmpURL=e.target.label;

 

                      varconn:NetConnection=newNetConnection();

 

                      conn.addEventListener(NetStatusEvent.NET_STATUS,netStatus);

 

                      conn.connect(rtmpURL);

 

              }

 

              privatefunctionnetStatus(e:NetStatusEvent):void{

 

                   Alert.show(e.info.code);

 

                   if(e.info.code=="NetConnection.Connect.Success"){

 

                        isConnectSuccess=true;

 

                   }

 

              }

 

         ]]>

 

     </mx:Script>

     <mx:Buttonx="25"y="10"label="rtmp://localhost/jiaocheng/"

 

click="this.clickConnect(event)"/>

 

     <mx:Buttonx="25"y="44"label="rtmp://localhost/jiaocheng/room/"

 

click="this.clickConnect(event)"/>

 

     <mx:Buttonx="25"y="84"label="rtmp://localhost/jiaocheng/room1/"

 

click="this.clickConnect(event)"/>

 

     <mx:Buttonx="25"y="120"label="rtmp://localhost/jiaocheng/room/place"

 

click="this.clickConnect(event)"/>

 

</mx:Application>

 

打开三个客户端,点击各个按钮看服务器端的输出。

 

..视频播放..

 

  这章将利用以前的知识做一个在线音乐视频播放的程序。当连接到服务器后会把服务器端的视频列表显

 

示出来,当用户点击后播放。尽管功能简陋,但稍加改进就可以成为产品。

 

  程序分服务器端和客户端。

 

  服务器端在第七章的基础上改进,新建类StreamService。代码如下

 

packagechapter7;

 

import java.io.File;

 

import java.io.IOException;

 

import java.text.SimpleDateFormat;

 

import java.util.Date;

 

import java.util.HashMap;

 

import java.util.Locale;

 

import java.util.Map;

 

import org.apache.commons.logging.Log;

 

import org.apache.commons.logging.LogFactory;

 

import org.red5.server.api.IScope;

 

import org.red5.server.api.Red5;

 

import org.springframework.core.io.Resource;

 

publicclassStreamService{

 

     /**

 

     *Getterforproperty'listOfAvailableFLVs'.

 

     *

 

     *@returnValueforproperty'listOfAvailableFLVs'.

 

     */

 

    publicMapgetListOfAvailableFLVs(){

 

         IScopescope=Red5.getConnectionLocal().getScope();

 

         Map<String,Map>filesMap=newHashMap<String,Map>();

 

         Map<String,Object>fileInfo;

 

         try{

 

              Resource[]flvs=scope.getResources("streams/*.flv");

               if(flvs!=null){

 

                    for(Resourceflv:flvs){

 

                        File file =flv.getFile();

 

                        DatelastModifiedDate=newDate(file.lastModified());

 

                        StringlastModified=formatDate(lastModifiedDate);

 

                        StringflvName=flv.getFile().getName();

 

                        StringflvBytes=Long.toString(file.length());

 

                        fileInfo=newHashMap<String,Object>();

 

                        fileInfo.put("name",flvName);

 

                        fileInfo.put("lastModified",lastModified);

 

                        fileInfo.put("size",flvBytes);

 

                        filesMap.put(flvName,fileInfo);

 

                    }

 

               }

 

               Resource[]mp3s =scope.getResources("streams/*.mp3");

 

               if(mp3s!=null){

 

                    for(Resourcemp3:mp3s){

 

                        File file =mp3.getFile();

 

                        DatelastModifiedDate=newDate(file.lastModified());

 

                        StringlastModified=formatDate(lastModifiedDate);

 

                        StringflvName=mp3.getFile().getName();

 

                        StringflvBytes=Long.toString(file.length());

 

                        fileInfo=newHashMap<String,Object>();

 

                        fileInfo.put("name",flvName);

 

                        fileInfo.put("lastModified",lastModified);

 

                        fileInfo.put("size",flvBytes);

 

                        filesMap.put(flvName,fileInfo);

 

                    }

 

               }

 

          }catch(IOExceptione){

 

               e.printStackTrace();

 

          }

 

          returnfilesMap;

 

     }

 

     privateStringformatDate(Datedate){

 

          SimpleDateFormatformatter;

 

          Stringpattern="dd/MM/yyH:mm:ss";

 

          Localelocale=newLocale("en","US");

 

          formatter=newSimpleDateFormat(pattern,locale);

 

          returnformatter.format(date);

 

     }

 

}

把编译后的文件拷贝到red5下;修改red5-web.xml,添加bean配置

 

     <beanid="flv.service"

 

         class="chapter7.StreamService"

 

         singleton="true"/>

 

删除idstreamFilenameGeneratorbean配置。在streams下放几个mp3flv格式的文件。

 

重启red5

 

新建FlexApplication代码如下:

 

<?xmlversion="1.0"encoding="utf-8"?>

 

<mx:Applicationxmlns:mx="http://www.adobe.com/2006/mxml"layout="absolute"

 

creationComplete="init()">

 

     <mx:Script>

 

         <![CDATA[

 

     import mx.events.ListEvent;

 

     import mx.collections.ArrayCollection;

 

     privatevarrtmpURL:String="rtmp://localhost/jiaocheng";

 

     privatevarnc:NetConnection=newNetConnection();

 

     privatevarplayerVideo:Video=newVideo();

 

    privatevarresponder:Responder=newResponder(getMediaList);

 

    [Bindable]

 

    publicvarvideoList:ArrayCollection;

 

     privatevarstream:NetStream;

 

     privatefunctioninit():void{

 

         playerVideo.x=200;

 

         connect();

 

     }

 

    privatefunctionconnect():void{

 

     nc.addEventListener(NetStatusEvent.NET_STATUS,netStatus);

 

     nc.connect(rtmpURL);

 

    }

 

    privatefunctionclickItem(e:ListEvent):void{

 

     container.addChild(playerVideo);

 

     stream.play(videoList.getItemAt(e.rowIndex).label);

 

    }

 

    publicfunctiongetVideos():void{

 

         nc.call("flv.getListOfAvailableFLVs",responder);

 

     }

 

    publicfunctiongetMediaList(list:Object):void{

 

         varmediaList:Array=newArray();

 

         for(varitems:Stringinlist){

 

              mediaList.push({label:items, size:list[items].size,

dateModified:list[items].lastModified});

 

         }

 

         videoList=newArrayCollection(mediaList);

 

    }

 

    privatefunctionnetStatus(e:NetStatusEvent):void{

 

         trace(e.info.code);

 

         if(e.info.code=="NetConnection.Connect.Success"){

 

             getVideos();

 

             stream=new NetStream(nc);

 

        stream.client=newStreamClient();

 

             playerVideo.attachNetStream(stream);

 

         }

 

    }

 

         ]]>

 

    </mx:Script>

 

    <mx:List itemClick="clickItem(event)"x="10"y="21"height="161"

 

dataProvider="{videoList}"></mx:List>

 

    <mx:UIComponentid="container">

 

    </mx:UIComponent>

 

</mx:Application>

 

运行客户端,可以看到一streams下的文件的名字。点击既可以播放。

 

附录:

 

Eclipse的使用

 

  新建工程:从菜单栏中选择文件菜单(file),然后选择新建(new)。从新建的类型里面选择java项目

 

javaproject)设置项目名称和保存的路径后点击确定然后点击下一步下一步就行了。

 

 添加库:点击新建的项目,选择菜单栏的工程(project),选择properties项,左边选择java buildpath

 

右边选择liberareis面板,点击addexternaljar,找到需要添加的jar,点击确定。

 

 Flexbuilder的使用

 

新建工程:file-->new-->newFlexproject-->填入工程名字-->finish

 

运行:ctrl+f11

 

调试:f11。调试可以看到trace();语句的输出。

 

        

 

0 0
原创粉丝点击