环境:RED5 0.8 WIN 安装版 ECLIPSE GALILEO 版 ACTIONSCRIPT 3.0
项目名称:webcam
----------------------------------------------------------------------------------------------
一、配置文件
1、red5-web.properties
配置内容:
webapp.contextPath=/webcam
webapp.virtualHosts=localhost, 127.0.0.1
2、red5-web.xml
配置内容:
默认的配置内容只需要改动一处即可:
<bean id="web.handler"
class="red5.Application"
singleton="true" />
注意:red5.Application 是我定义的应用类包路径,要与JAVA代码对应
3、web.xml
默认的配置内容只需要改动一下上下文名称即可
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>/webcam</param-value>
</context-param>
注意:上下文名称是你的项目名也是发布路径
二、JAVA应用代码:
package red5;
import org.red5.server.adapter.MultiThreadedApplicationAdapter;
import org.red5.server.api.IConnection;
import org.red5.server.api.IScope;
import org.red5.server.api.Red5;
import org.red5.server.stream.ClientBroadcastStream;
public class Application extends MultiThreadedApplicationAdapter {
//create a instance for application
public Application app;
public IConnection conn;
public IScope scope;
public ClientBroadcastStream stream;//用来接受flash上传的stream的类
//start the recording
//客户端call这个function要传入流的名字
public String startRecord(String streamName){
//start a new recording
String fileName = String.valueOf(System.currentTimeMillis());
app = new Application();
conn = Red5.getConnectionLocal();//得到当前的连接
scope = conn.getScope();//一组连入服务器的客户
app.connect(conn,scope,null);
System.out.println("connection Established!");
//注意stream name,在flash端也需要匹配
stream = (ClientBroadcastStream)app.getBroadcastStream(scope,streamName);
System.out.println("The publisher's name is: "+stream.getPublishedName()+", created at: "+stream.getCreationTime());
System.out.println("the stream Name is: "+fileName);
try{
stream.saveAs(fileName, false);
}catch (Exception e){
System.out.println(e.toString());
}
return fileName;
}
//stop the recording
public String stopRecord(){
stream.stopRecording();//停止记录
System.out.println("byte Recieved: "+stream.getBytesReceived());
System.out.println("Recording Stopped!");
return "Server stop recording!";
}
}
三、Actionscript 3.0 代码
import flash.display.MovieClip;
import flash.events.*;
import flash.media.Camera;
import flash.media.Microphone;
import flash.media.Video;
import flash.media.SoundCodec;
import flash.net.NetStream;
import flash.net.NetConnection;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
var resp:Responder=new Responder(onResult);
var _video:Video;
var _cam:Camera;
var _mic:Microphone;
var _nc:NetConnection;
var _ns:NetStream;
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
createChildren();
initConn();
function createChildren():void {
_cam=Camera.getCamera();
_cam.setQuality(144000, 85);
_cam.setMode(320, 240, 15);
_cam.setKeyFrameInterval(60);
_video=new Video();
_video.attachCamera(_cam);
addChild(_video);
_mic=Microphone.getMicrophone();
if(_mic != null){
_mic.setSilenceLevel(0,-1);
_mic.gain = 80;
_mic.setLoopBack(true);
}
}
function initConn():void{
_nc=new NetConnection();
_nc.objectEncoding = ObjectEncoding.AMF3;
_nc.client=this;
_nc.addEventListener(NetStatusEvent.NET_STATUS , netStatus);
_nc.connect("rtmp://localhost/webcam/",true);
}
function publish():void {
if(_nc.connected){
_ns=new NetStream(_nc);
_ns.addEventListener(NetStatusEvent.NET_STATUS , netStatus);
_ns.attachCamera(_cam);
_ns.attachAudio(_mic);
_ns.publish("mystream", "live");
}
}
function netStatus (event:NetStatusEvent):void{
if ( event.info.code == "NetConnection.Connect.Success"){
publish();
}
}
function onResult(obj:Object):void{
;
}
b_start.addEventListener(MouseEvent.CLICK,btStart);
function btStart(Event:MouseEvent){
_nc.call("startRecord",new Responder(getInfor,onState),"mystream");
}
function getInfor(reobj:Object):void {
trace("Server returning Infor: "+reobj);
}
function onState(err:Object):void {
trace("Connection result error: "+err);
}
b_stop.addEventListener(MouseEvent.CLICK,btStop);
function btStop(Event:MouseEvent){
_nc.call("stopRecord",new Responder(getInfor,onState));
}
注意:FLASH只有一桢,b_start 与b_stop是我用MC做的一个按钮的实例名称。一个开始录制一个停止录制.
red5 api之IScope接口的理解
IScope 接口定义了Red5中作用域对象.该对象维护了一个由一组客户端连接组成的上下文状态.通过作用域对象我们就可以很轻松的实现一个分级访问、区域对象的共享的功能.那么,对于一个作用域对象它可以有父作用域对象,也可以有子作用域对象.如果一个客户端连接到了一个作用域对象,同时也连接到了它的父作用域对象.通过作用域对象就可以访问资源、共享对象、视音频流等.作用域对象在应用程序中定义了一些组选项.
下面是作用域所有的名称application,room,place,lobby.
下面简单介绍一下IScope接口的方法
boolean addChildScope(IBasicScope scope)
描述添加一个子作用域.如果添加成功返回True,如果添加的子作用域已经是该作用域的子作用域,那么返回False.
参数scope 一个子作用域对象.
返回值True添加成功,False添加失败,添加的子作用域已经是该作用域的子作用域.
boolean connect(IConnection conn)
描述添加一个连接对象.
参数conn一个连接对象.
返回值True表示成功,如果该连接对象已经属于该作用域对象则返回False.
boolean connect(IConnection conn,Object[] params)
描述添加一个连接对象,并传入相应的参数对象.
参数conn连接对象.
params参数对象.
返回值True表示成功.如果该连接对象已经属于该作用域对象则返回False.
boolean createChildScope(String name)
描述通过一个字符串名称创建一个子作用域对象.成功返回True,如果该子对象已经存在本作用域中,则返回False.
参数name 子作用域的名称.
返回值True表示创建成功.如果该子对象已经存在本作用域中,则返回False.
void disconnect(IConnection conn)
描述从该作用域对象的连接对象列表中删除一个指定的连接对象.这样就会把所有提供该连接对象的客户端和本作用域断开连接.
参数提供的连接对象..
IBasicScope getBasicScope(String type,String name)
描述获得一个子作用域对象.
参数type子作用域的类型.
name子作用域的名称.
返回值如果指定子作用域对象存在返回该对象,否则返回Null.
IteratorString getBasicScopeNames(String type)
描述获得所有指定类型的子作用域.
参数类型名称.
返回值返回一个范型的迭代器,通过该迭代器可以获得所有子作用域对象.
SetIClient getClients()
描述返回当前作用域对象中所有子作用域对象的范型集合.
返回值所有子作用域对象的范型集合.
IteratorIConnection getConnections()
描述获得本作用域所有连接对象的范型迭代器.
返回值连接对象迭代器.
IContext getContext()
描述返回本作用域上下文环境.
返回值返回本作用域上文对象.
String getContextPath()
描述返回上下文路径.
返回值上下文路径.
IScopeHandler getHandler()
描述获得该作用域对象的控制器对象.
返回值作用域的控制器对象.
IScope getScope(String name)
描述通过名称获得作用域对象.
参数name 作用域对象名称.
返回值指定名称的作用域对象.
IteratorString getScopeNames()
描述获得所有子作用域的名称迭代器.
返回值子作用域名称迭代器.
boolean hasChildScope(String name)
描述判断当前作用域是否有指定名称的子作用域.
参数name 子作用域的名称.
返回值如果存在返回True,反之返回False.
boolean hasChildScope(String type, String name)
描述通过指定类型和名称判断当前作用域是否有指定名称的子作用域.
参数type 子作用域的类型.
name 子作用域的名称.
返回值如果存在返回True,反之返回False.
boolean hasHandler()
描述判断该作用域是否存在控制器.
返回值存在返回True,反之返回False.
SetIConnection lookupConnections(IClient client)
描述通过客户端对象,查找连接对象.
返回值返回只读的连接对象集合的迭代器.
void removeChildScope(IBasicScope scope)
描述删除指定的子作用域.
参数子作用域对象.
red5的常用方法
在使用red5+flash做rpg游戏开发的时候有些功能是需要服务器来提供的,如最简单的多人用户上线,当一个用户上线后后要通知所有其他的用户,这个时候就需要red5去获取所用链接的客户端,然后通知客户端用户上线。当用户下线的时候,也是同样的需要red5的支持。 我在开发的时候是使用red5 0.8的版本。 下面详细介绍一下具体的实现。 ApplicationAdapter是客户端与red5连接的基础 ...
在使用red5+flash做rpg游戏开发的时候有些功能是需要服务器来提供的,如最简单的多人用户上线,当一个用户上线后后要通知所有其他的用户,这个时候就需要red5去获取所用链接的客户端,然后通知客户端用户上线。当用户下线的时候,也是同样的需要red5的支持。
我在开发的时候是使用red5 0.8的版本。
下面详细介绍一下具体的实现。
ApplicationAdapter是客户端与red5连接的基础类:
下面来介绍一下调用的顺序,和建立so的方法。
第一步:
RED5服务器启动后,开始加载,此时只能获取应用的SCOPE
public boolean appStart(IScope arg0) {
this.createSharedObject(arg0, "point", true);
ISharedObject so = this.getSharedObject(arg0, "point");//建立一个so
if (so != null)
so.addSharedObjectListener(new ShareObjectListener());
return true;
}
改方法中的ShareObjectListener监听器是一个需要实现ISharedObjectListener接口的类。这样就建立一个so了。
第二步:
响应连接处理,可获取当前connection的信息,并且可以接收客户端提供的参数
public boolean appConnect(IConnection conn, Object[] params){
return true;
}
第三步:连接成功,此时可获取client信息和SCOPE信息
public boolean appJoin(IClient client, IScope app){
return true;
}
@Override
public boolean roomStart(IScope arg0) {
System.out.println(" 启动roomStart");
return true;
}
如果需要获取每一个room中的客户端连接。
@Override
public boolean roomConnect(IConnection arg2, Object[] arg1) {
IScope arg0 = arg2.getScope();
Set<IClient> i = arg0.getClients();
for (IClient c : i) {
callClient(c.getConnections().iterator().next());
}
return true;
}
public void callClient(IConnection conn) {
if (conn instanceof IServiceCapableConnection) {
IServiceCapableConnection sc = (IServiceCapableConnection) conn;
sc.invoke("resultFun",new Object[]{peopleArray},this);
}
}
最后一步:
客户端浏览器关闭即退出应用。
public void appDisconnect(IConnection conn) {
return;
}
转几篇Red5的日志配置
按这几篇文章配置,基本上就能搞定了。
一、第一篇中文的,开始。。。。
首先不能完全 按照log4j的配置去搞log4j.properties,因为他们是用 log4j+slf4j 搞在一起的
在 classes下面要有个logback-myapp.xml的文件,里面配置
<?xml version=”1.0″ encoding=”UTF-8″?>
<configuration>
<appender name=”RED5DEMO” class=”ch.qos.logback.core.FileAppender”>
<File>log/red5demo.log</File>
<Append>false</Append>
<Encoding>UTF-8</Encoding>
<BufferedIO>false</BufferedIO>
<ImmediateFlush>true</ImmediateFlush>
<layout class=”ch.qos.logback.classic.PatternLayout”>
<Pattern>
%date [%thread] %-5level %logger{35} - %msg%n
</Pattern>
</layout>
</appender>
<root>
<level value=”DEBUG” />
<appender-ref ref=”RED5DEMO” />
</root>
<logger name=”org.red5.demos.red5demo”>
<level value=”DEBUG” />
</logger>
</configuration>
重启red5 在 red5/log 下看到一个red5demo.log文件了。
二.再看另外一篇文章:
1、概述:
Red5起初使用的log4j日志系统,现在使用的是logback日志系统。
Ceki在Java日志领域世界知名。他创造了Log4J,这个最早的Java日志框架即便在JRE内置日志功能的竞争下
仍然非常流行。随后他又着手实现SLF4J这个“简单的日志前端接口(simple logging facade for java)”来替代JCL(Jakarta Commons-Logging)。这几年Ceki在从事他的新项目,LOGBack,一个“可靠、通用、快速而又灵活的Java日志框架”。
主要函数有:
package org.slf4j;
public interface Logger {
// Printing methods:
public void trace(String message);
public void debug(String message);
public void info(String message);
public void warn(String message);
public void error(String message);
}
主要日志级别有:TRACE DEBUG INFO WARN ERROR OFF
2、Red5LoggerFactory类:
red5-svn\java\server\trunk\src\org\red5\logging\Red5LoggerFactory文件中定义了”Red5LoggerFactory”类。
它简化了获取日志实例的请求操作,在Red5应用程序中推荐使用。
public class Red5LoggerFactory {
public static Logger getLogger(Class clazz) {
String contextName = null;
return getLogger(clazz, contextName);
}
public static Logger getLogger(Class clazz, String contextName) {
…
}
}
getLogger()是个重载函数。
*)如果只传递一个参数,则返回默认context的日志系统,即”red5.log”。
Red5源码中一般都是调用第一个函数,如下所示:
private static Logger log = Red5LoggerFactory.getLogger(TomcatLoader.class);
*)如果传递两个参数,则返回指定webapp context的日志系统,即”xxx.log”。
Red5的应用程序一般都是调用第二个函数,如下所示:
private static Logger log = Red5LoggerFactory.getLogger(Application.class, “oflaDemo”);
(这里有一点要注意,xuggle的例子貌似调用的不对:
final private Logger log = Red5LoggerFactory.getLogger(this.getClass());)
3、注意事项:
*)只要修改了web.xml文件,添加了日志侦听,就可生成指定的日志文件xxx.log。
*)logback-webapp.xml这个配置文件必要要放对位置,即jar打包文件的根目录或者class目录下。
(否则即使生成了xxx.log文件,后续日志也无法写进去。)
*)java源程序里要正确返回指定的日志系统,即使用Red5LoggerFactory类的两个参数的getLogger函数。
(否则返回的是Red5默认的日志系统,即写到red5.log文件里)
三、具体怎么用写日志,再看:
Logging Setup
The logging system uses Simple Logging Facade for Java ( SLF4J). This framework supports many of the logging systems available for Java and also provides simple implementations. The logging used by our dependencies are mainly Log4j and Apache commons logging and SLF4J allows us to combine them into one system. SLF4J gives you the ability to select a logging implementation and provides proxies for you dependencies if their maintainers did not select the same framework.
We prefer the logback log implementation, but you may use whatever you like. There are some hoops you will have to jump through to get Log4j or Commons logging to work. Blog post about using other loggers here.
After you chose an implementation framework, some of the SLF4J jars must NOT be in your applications classpath or they will cause conflicts. The default case it to use Logback, so the following jars must be included:
slf4j-api - The core APIlogback-core - Current Logback core librarylogback-classic - Logback support librarylog4j-over-slf4j - Log4j proxy/bridgejcl-over-slf4j - Apache commons logging proxy/bridgejul-to-slf4j - java.util.logging proxy/bridge
The items denoted as “proxy/bridge” listen for the logging calls to those implementations and pass them through to SLF4J.
The following two strategies are to be consider untested.
If you prefer to use Log4j instead, the following jars are required:
slf4j-api - The core APIlog4j - Current Log4j library (1.2+)slf4j-log4j12 - Log4j adapterjcl-over-slf4j - Apache commons logging proxy/bridgejul-to-slf4j - java.util.logging proxy/bridge
If you prefer to use Commons logging the following jars are required:
slf4j-api - The core APIcommons-logging - Apache commons logging libraryslf4j-jcl - Commons logging adapterlog4j-over-slf4j - Log4j proxy/bridgejul-to-slf4j - java.util.logging proxy/bridge
If you want to use another implementation not shown here, simply check out the faq SLF4J FAQ
Logback is the successor of Log4j and was created by the creator of Log4j and SLF4J. A conversion tool has been created for your log4j properties files configuration converter There is also an eclipse console plugin eclipse console plugin.
Web applications
In your web applications remove the following entry from your web.xml
<context-param><param-name>log4jConfigLocation</param-name><param-value>/WEB-INF/log4j.properties</param-value></context-param>Add the following to the web.xml
<listener><listener-class>org.red5.logging.ContextLoggingListener</listener-class></listener><filter><filter-name>LoggerContextFilter</filter-name><filter-class>org.red5.logging.LoggerContextFilter</filter-class></filter><filter-mapping><filter-name>LoggerContextFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>You should also:
Remove any “log4j” listeners from the web.xmlRemove any log4j.properties or log4j.xml filesCreate a logback-myApp.xml where myApp is the name for your webapp and place it on your webapp classpath (WEB-INF/classes or in your application jar within WEB-INF/lib)Set your display-name in the web application to match the context name you will be using (Use the example oflaDemo as a guide).Ensure that the contextName and jmxConfigurator have the correct context name, this is the name of your web application
Sample webapp logback config file (logback-myApp.xml), not to be confused with the red5 log config file located in /conf
<?xml version=”1.0″ encoding=”UTF-8″?><configuration><contextName>myApp</contextName><jmxConfiguratorcontextName=“myApp”/><appendername=“FILE”class=“ch.qos.logback.core.FileAppender”><File>example.log</File><Append>false</Append><BufferedIO>false</BufferedIO><ImmediateFlush>true</ImmediateFlush><layoutclass=“ch.qos.logback.classic.PatternLayout”><Pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</Pattern></layout></appender><root><levelvalue=“DEBUG“/><appender-refref=“FILE”/></root><loggername=“com.example”><levelvalue=“DEBUG“/></logger></configuration>Reminder replace everything that says “myApp” with your application name.
Imports
When using logback and slf4j, your imports should consist only of the following for a non webapp class:
importorg.slf4j.LoggerFactory;importorg.slf4j.Logger;It is suggested that you use Red5LoggerFactory in-place of LoggerFactory to ensure that your application gets the correct logger.
For loggers inside your webapp imports should be:
importorg.slf4j.Logger;importorg.red5.logging.Red5LoggerFactory;Logger Instantiation
For non webapp classes:
To log to a “root” logger, change all your logger instantiation statements to:
privatestaticLogger log=Red5LoggerFactory.getLogger(MyClassName.class);Reminder replace “MyClassName” with the name of the class itself.
To log to a “context” logger, change all your logger instantiation statements to:
privatestaticLogger log=Red5LoggerFactory.getLogger(MyClassName.class,“myApp”);Reminder replace “myApp” with the name of the context; “myApp” would become “oflaDemo” for the oflaDemo application.
Your old instantiations probably resemble this:
privatestaticLogger log=Logger.getLogger(MyClassName.class.getName());Your applications logging configuration file must contain the name of your application context in its file name; For instance the “oflaDemo” uses the configuration logback-oflaDemo.xml.
Lastly, as an optimation change your log statements to:
log.debug(“Here is a log message for an object {}”, myobject);You no longer need to concatenate strings when logging, if you need more than one variable do the following:
log.debug(“Here is a log message with a couple vars {} or {} or {}”,new Object[]{object1, myobject, object3});red5 cluster 集群
red5的开发者真是太伟大了,把集群搞的这么平民化。
1. 下载red50.8
2. 修改一下build.xml ant dist-cluster
3. 把red5/home/cluster 的edge和origin 部署到你的edge和origin Server上。
比如2(2个edge)-2(2个origin),就需要4台机器
edge1
edge2
origin1(ip:origin1)
origin2(ip:origin2)
4. 修改edge Server上的
red5-edge-core.xml
把下面
<bean id="mrtmpClient"
class="org.red5.server.net.mrtmp.MRTMPClient" init-method="start" >
<property name="ioHandler" ref="mrtmpHandler" />
<property name="server" value="${mrtmp.server}" />
<property name="port" value="${mrtmp.port}" />
</bean>
修改为
<bean id="mrtmpClient"
class="org.red5.server.net.mrtmp.MRTMPClient" init-method="start" >
<property name="ioHandler" ref="mrtmpHandler" />
<property name="server" value="${origin1}" />
<property name="port" value="${mrtmp.port}" />
</bean>
<!-- 如果你只有3台机器 或只需要部署一个origin 请把下面这段注释掉就ok了 -->
<bean id="mrtmpClient2"
class="org.red5.server.net.mrtmp.MRTMPClient" init-method="start" >
<property name="ioHandler" ref="mrtmpHandler" />
<property name="server" value="${origin2}" />
<property name="port" value="${mrtmp.port}" />
</bean>
5. 启动你的2个origin和2个edge
6.测试你的集群是否成功
在origin机器上
开2个browser(浏览器)
http://localhost:5080/demos/BallControl.html
http://localhost:5080/demos/BallControl.html
把rtmp的ip分别改为 edge1的 和 edge2 的
移动那个球试试看。你一定会部署成功的。