Red5研究

最近因为工作需要,暂停手机视频直播系统的学习和开发,转为完成一个比较急的任务:将从DVR得到的视频通过网页的flash player插件来直播。

    要完成这个任务,首先必须对基于RTMP的流媒体服务器有一定的了解。以前我有花了一段时间学Flash Media Server,不过因为FMS卖得很贵,所以还是用免费的Red5好了。

    读了研究生之后,我就进入了图像处理领域,写程序都是用matlab、C、C++,本来以为本科时候写的7万行java代码要浪费了,现在发现还是挺有用的,因为Red5是java写的。Java代码看起来比C、C++容易。

    下面开始介绍Red5:

Red5是建立在Jetty(servlet engine) , Mina (networking)基础之上的,参考了Terracotta Clustering技术理念,并通过使用Spring框架将起整合起来的。

Jetty是一款运行速度较快轻量级的开源servlet 容器,可以作为嵌入式服务使用。

Mina 是Apache组织下的一个基于 Java NIO 技术的新项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架。

Terracotta是开源的Java集群技术。

Spring是轻量级Java企业级开发的事实标准,Red5采用Spring来管理使程序逻辑更加清晰,耦合性更低,扩展性更好。

 

Red5响应请求的流程

(1)Red5在启动时会调用RTMPMinaTransport的start()方法,该方法会开启rmtp的socket监听端口(默认是1935),然后使用Mina 的api 将RTMPMinaIoHandler 绑定到该端口。

(2)RTMPMinaIoHandler 上定义了messageReceived()、messageSent()、sessionOpened()和sessionClosed()等方法,当有socket请求时,相应的方法会被调用。这时RTMPMinaIoHandler 会使用当前的socket连接来创建一个(或者使用之前创建好的)RTMPMinaConnection,并将其作为参数传递给定义于RTMPHandler类上的相应的messageReceived()、messageSent()、connectionOpened()和connectionClosed()方法。

(3)RTMPHandler会调用Server类的lookupGlobal()方法获得当前的GlobalScope,然后再利用GlobalScope找到当前socket请求应该使用的WebScope(这个WebScope 由开发者在WEB-INF\red5-web.xml中定义)。最后,RTMPHandler会调用RTMPMinaConnection 的connect ()方法连接到相应的WebScope。

(4)通常来说,WebScope又会将请求转移给ApplicationAdapter,由它来最终响应请求,而项目中通过重载ApplicationAdapter的方法来实现自己的逻辑。至此,控制流进入了开发者的项目中。

 

ApplicationAdapter类介绍

MultiThreadedApplicationAdapter类,是Red5应用程序的基础类。它提供了操作SharedObjects 和 streams的方法,还有连接和服务列表,是一个应用程序基本的Iscope。它实现了IstreamAwareScopeHandler接口,提供了在应用程序种控制流的方法。它还提供了一个很有用的事件控制器,可以拦截流、授权用户访问等。可以在其子类中添加各种方法,在客户端上通过NetConnection.call()方法调用服务器端的方法。这与Flash Media Server不同,FMS需要将你的客户端方法保存在服务器端,Red5提供更方便的方式进行远程方法调用。

ApplicationAdapter类是MultiThreadedApplicationAdapter的子类。ApplicationAdapter使用单线程的方式进行方法调用的消息出来,所以其性能不如MultiThreadedApplicationAdapter。

这2个类的核心方法为:

public boolean appStart(IScope arg0):Red5应用程序启动时自动执行此方法,进行一些参数初始化,如声明连接数据库的资料等。

public void appStop(IScope arg0):Red5应用程序停止时自动执行此方法。

public boolean appConnect(IConnection arg0, Object[] arg1):当客户端连接本应用程序时自执行此方法。

public boolean appDisconnect(Iconnection conn):当客户端断开连接时自动执行此方法。(如关闭浏览器、关闭FLASH PLAYER等特殊情况,均会触发该方法)。

public boolean appJoin(IClient arg0, IScope arg1):当有新的连接加入进来时自动调用。

获取某客户在服务器端保留/设置的变量:首先要通过Red5.getConnectionLocal()方法获得Iconnection对象,然后调用它的getClient()方法获得Client对象,再通过Client对象的getAttributes(“var_name”) 方法获得对应的变量的值。

 

下面是继承ApplicationAdapter类实现自己的Red5应用程序的代码例子:

 

import org.red5.server.adapter.ApplicationAdapter;

import org.red5.server.api.IClient;

import org.red5.server.api.IConnection;

import org.red5.server.api.IScope;

 

public class Application extends ApplicationAdapter

{

    @Override

  public boolean appStart(IScope arg0)  // 应用程序启动时自动执行

  {

        return super.appStart(arg0);

    }

    @Override

  public void appStop(IScope arg0)  // 应用程序停止时自动执行

  {

        super.appStop(arg0);

    }

    @Override

  public boolean appConnect(IConnection arg0, Object[] arg1)  // 客户端连接本应用程序时自执行

  {

        if(arg1 != null){

            for(Object o:arg1){

               …

            }

        }

        return super.appConnect(arg0, arg1);

    }

    @Override

  public boolean appJoin(IClient arg0, IScope arg1)  // 有新的连接加入进来时自动调用

  {

        //client.setAttribute("username",username);

        //IConnection current = Red5.getConnectionLocal();

        //current.getClient().getAttribute("username")

        //Iterator<IConnection> it=appScope.getConnections();

        //var nc:NetConnection=new NetConnection();

        //nc.connect("rtmp://127.0.0.1/your_app","your_vars");

        return super.appJoin(arg0, arg1);

    }

}

 

 

 

主要API简介

Red5提供了很多接口供开发人员进行二次开发:

IConnection:连接对象。每个连接都有一个关联的客户端和域。连接可能是持续型、轮询型、或短暂型。建立此接口的目的,是为了给不同的子类,如 RTMPConnection,RemotingConnection,AJAXConnection, HttpConnection 等,提供基础通用的方法。它提供getClient()方法来获取客服端对象。

IScope :每个Red5应用程序至少有一个域,用来搭建处理器、环景、服务器之间的连接。域可以构成树形结构,所有客户端都可以作为其节点共享域内的对象(比如流和数据)。所有的客服端(client)通过连接(connection)连接到域中。对于单一域,每个连接对应一个客服端,每个客服端对应一个id,简单的应用,操作就针对一个id和一个连接进行。

IServiceCapableConnection :获取有效连接。代码中先获取到连接实例,然后判断是否是有效连接并强制类型转换,之后调取客户端相应函数。

IClient :客户端对象代表某单一客户端。一个客户端可以和同一主机下不同的域分别建立连接。客户端对象和HTTP session 很相像。可以使用IClientRegistry.newClient(Object[])方法来创建IClient对象。

ApplicationAdapter:ApplicationAdapter是应用层级的IScope。若要处理流进程,需实现 IStreamAwareScopeHandler接口中的相应处理方法。ApplicationAdapter还提供了有效的事件处理机制,来进行截取流、确认用户等操作。同时,其子类中引入的方法均可在客户端通过 NetConnection 调取。在Aodbe 的FMS 中必须在服务器端维护客户端对象,与之相较,Red5 为您的远程请求提供了更加方便快捷的操作方法。

 

Red5开发示例

开发一个Red5 应用的服务器项目,和一般的J2EE 程序相似。进入Red5的webapps目录下,参考doc/templates目录下的myapp 文件夹,新建一个以自己项目命名的文件夹,比如myFirst,并对配置文件做相应的修改。然后开始编写自己的服务器端程序:

 

public class MyApplication extends ApplicationAdapter

{

  private IScope appScope; 

  private String username=””;

 

  // 取得本次连接的Iscope, appStart方法在连接开始时自动触发

  public boolean appStart(IScope app)

  {

    appScope = app;

    return true;

  }

 

  // 连接时触发的函数,定义本过程中的username

  public boolean appConnect(IConnection conn, Object[]params)

  {

    username = (String)params[0];

    return true;

  }

  // 连接加入时触发的函数,写入username的值

  public boolean appJoin(IClient client, IScope app)

  {

    client.setAttribute(”username”,username);

    return true;

  }

  // 客户端登录时调用函数,将返回目前登录的在线列表

  public String login()

  {

    IConnection current = Red5.getConnectionLocal();

    System.out.println(”<—”+current.getClient().getId()+”:”

    +current.getClient().getAttribute(”username”));

    return getOnlineList();

  }

  // 取得在线列表,对在线的客户端进行遍历,并显示。

  public String getOnlineList()

  {

    Iteratorit=appScope.getConnections();

    StringonLineList=””;

    while(it.hasNext())

    {

      IConnectionthis_conn=it.next();

      IClientic=this_conn.getClient();

      Stringu=ic.getAttribute(”username”).toString();

      onLineList+=ic.getId()+”,”+u+”;”;

      System.out.println(u);

    }

    return onLineList;

  }

 

 

 

我们还需要对刚刚完成的程序进行配置,编辑WEB-INF下的red5-web.xml,增加<bean id=”web.handler”  class=”MyApplication”>这样本程序将以刚才写的Application来运行。编辑web.xml,找到webAppRootKey,将其下面的paramvalue标签中的值修改为/myFirst,编辑red5-web.properties,将第一句修改为webapp.contextPath=/myFirst。

这样,在客户端中就可以轻易的调用login()方法了。在FLASH 中新建一个文件,输入以下代码:

 

nc = new NetConnection();

nc.connect(”rtmp:// localhost/myFirst”);

nc.onResult = function(obj)

{

  trace(obj);

}

nc.call(”login”,nc);

 

 

客户端登录服务器,需要调用login方法,服务器便能得到一份客户端的在线列表了。

至于建立和运行一个Red5应用程序的具体过程,这里就不讲了。

猜你喜欢

转载自gongminrui.iteye.com/blog/1717330