《Red5 用户参考手册》之十二:Red5 核心技术第五章 脚本实现
官方最新《Red5 用户参考手册》全套下载地址
I. 选择一个脚本实现
级别:初级
RED5 含有以下脚本语言的解释器:
* Javascript - version 1.6 (Mozilla Rhino version 1.6 R7)
* JRuby - version 1.0.1 (Ruby version 1.8.5)
* Jython - version 2.2 (Python version 2.1)
* Groovy - version 1.0
* Beanshell - version 2.0b4
RED5 的以后版本可能会含有以下脚本语言的解释器:
* JudoScript
* Scala
* PHP(这个不一般,我们可能会只是提供一个桥梁)
* Actionscript (可能会是 SSAS)
文件的位置:/META-INF/services/javax.script.ScriptEngineFactory
读取自定义在 jdk 或 jre 的类会比在其他地方定义的类优先考虑。
II. 配置 Spring
级别:中级
第一步是定位你的 web 应用 red5-web.xml 文件。在这个 xml 配置文件中 web.scope bean 的定义必须提供一个 web.handler,这个处理程序就是你的 Red5 应用(一个必须扩展 org.red5.server.adapter.ApplicationAdapter 类的应用)。
这个应用提供对 Red5 服务器的访问,并提供已创建的所有服务实例。服务实例和应用本身可以使用脚本。定义在 Spring 配置文件中的 bean 不能具有相同的 id,这里是一些 web 处理程序的定义示例:
一般来说,使用脚本类的配置文件是使用构造函数的参数(见解释部分),其顺序如下:
* 参数 1 - 脚本源文件的位置
* 参数 2 - 脚本实现的 Java 接口。扩展了应用程序的接口基本样板请参考上面的例子。你不需要在你所有脚本定义里都使用这些接口。
* 参数 3 - 脚本扩展的 Java 类。扩展的类并不总是必要,这取决于脚本引擎的实现。
示例 classpath 位置:物理磁盘里的 "oflaDemo" 依赖的应用等同于 webapps/oflaDemo/WEB-INF/applications。
III. 创建一个应用脚本
1.应用适配器
一些语言中,使用脚本编写应用程序适配器比在其他语言中要难得多,藉此我介绍 Ruby 的例子,它的效果很好并易于编写和整合。这个应用服务在其他支持的语言中也很容易编写,但它们至少需要 Java 接口。
2.应用服务
这里是一个 Java 接口的示例(是的,假设方法是空的),这个接口用于为搜集一系列的文件并将它们以 Map(名-值 对)返回给调用者的应用提供一个模板。
IV. 创建你自己的解释器
级别:高级
在打开这个话题之前先提一下上周末 02/2007 试图建立一个PHP解释器,那阵势一个痛苦的过程哈,四个小时之后我不得不放弃。这件事我汲取的教训就是你首先得断定你的脚本语言可以作为应用程序运行,而不是作为 HTTP 请求处理器。这里可以先测试一下:X 语言是否可以编译成一个可执行程序,或者它可以使用命令行运行?如果可以的话,那它就可以进行轻易整合。
V. 脚本信息的相关链接
* Spring 脚本
http://static.springframework.org/spring/docs/2.0.x/reference/dynamic-language.html
http://rhinoinspring.sourceforge.net/
* Java 脚本
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/
http://blogs.sun.com/sundararajan/
https://scripting.dev.java.net/
http://today.java.net/pub/a/today/2006/04/11/scripting-for-java-platform.html
http://www.javaworld.com/javaworld/jw-03-2005/jw-0314-scripting_p.html
http://www.oreillynet.com/onjava/blog/2004/01/java_scripting_half_the_size_h.html
http://www.robert-tolksdorf.de/vmlanguages.html
* Javascript
http://www.mozilla.org/rhino/
http://www.mozilla.org/rhino/ScriptingJava.html
* Ruby
http://jruby.codehaus.org/
* BeanShell
http://www.beanshell.org/
* Python
http://www.jython.org/Project/
http://www.onjava.com/pub/a/onjava/2002/03/27/jython.html
http://jepp.sourceforge.net/
http://jpe.sourceforge.net/
http://jpype.sourceforge.net/
* Groovy
http://groovy.codehaus.org/
原文链接: http://trac.red5.org/wiki/Documentation/UsersReferenceManual/Red5CoreTechnologies/05-Scripting-Implementations。
I. 选择一个脚本实现
级别:初级
RED5 含有以下脚本语言的解释器:
* Javascript - version 1.6 (Mozilla Rhino version 1.6 R7)
* JRuby - version 1.0.1 (Ruby version 1.8.5)
* Jython - version 2.2 (Python version 2.1)
* Groovy - version 1.0
* Beanshell - version 2.0b4
RED5 的以后版本可能会含有以下脚本语言的解释器:
* JudoScript
* Scala
* PHP(这个不一般,我们可能会只是提供一个桥梁)
* Actionscript (可能会是 SSAS)
脚本实现类根据你的 Java 版本预先指定在以下位置:
Java5 - js-engine.jar, jython-engine.jar, groovy-engine.jar
Java6 - resources.jar
文件的位置:/META-INF/services/javax.script.ScriptEngineFactory
读取自定义在 jdk 或 jre 的类会比在其他地方定义的类优先考虑。
II. 配置 Spring
级别:中级
第一步是定位你的 web 应用 red5-web.xml 文件。在这个 xml 配置文件中 web.scope bean 的定义必须提供一个 web.handler,这个处理程序就是你的 Red5 应用(一个必须扩展 org.red5.server.adapter.ApplicationAdapter 类的应用)。
这个应用提供对 Red5 服务器的访问,并提供已创建的所有服务实例。服务实例和应用本身可以使用脚本。定义在 Spring 配置文件中的 bean 不能具有相同的 id,这里是一些 web 处理程序的定义示例:
* Java 实现类
<bean id="web.handler" class="org.red5.server.webapp.oflaDemo.MultiThreadedApplicationAdapter" />
* Javascript 实现
<bean id="web.handler" class="org.red5.server.script.rhino.RhinoScriptFactory">
<constructor-arg index="0" value="classpath:applications/main.js"/>
<constructor-arg index="1">
<list>
<value>org.red5.server.api.IScopeHandler</value>
<value>org.red5.server.adapter.IApplication</value>
</list>
</constructor-arg>
<constructor-arg index="2">
<value>org.red5.server.adapter.ApplicationAdapter</value>
</constructor-arg>
</bean>
* Ruby 实现
<bean id="web.handler" class="org.springframework.scripting.jruby.JRubyScriptFactory">
<constructor-arg index="0" value="classpath:applications/main.rb"/>
<constructor-arg index="1">
<list>
<value>org.red5.server.api.IScopeHandler</value>
<value>org.red5.server.adapter.IApplication</value>
</list>
</constructor-arg>
</bean>
* Groovy 实现
<bean id="web.handler" class="org.red5.server.script.groovy.GroovyScriptFactory">
<constructor-arg index="0" value="classpath:applications/main.groovy"/>
<constructor-arg index="1">
<list>
<value>org.red5.server.api.IScopeHandler</value>
<value>org.red5.server.adapter.IApplication</value>
</list>
</constructor-arg>
</bean>
* Python 实现
Red5 Open Source
Flash Server (0.7.1) 51
<bean id="web.handler" class="org.red5.server.script.jython.JythonScriptFactory">
<constructor-arg index="0" value="classpath:applications/main.py"/>
<constructor-arg index="1">
<list>
<value>org.red5.server.api.IScopeHandler</value>
<value>org.red5.server.adapter.IApplication</value>
Scripting Implementations
</list>
</constructor-arg>
<constructor-arg index="2">
<list>
<value>One</value>
<value>2</value>
<value>III</value>
</list>
</constructor-arg>
</bean>
一般来说,使用脚本类的配置文件是使用构造函数的参数(见解释部分),其顺序如下:
* 参数 1 - 脚本源文件的位置
* 参数 2 - 脚本实现的 Java 接口。扩展了应用程序的接口基本样板请参考上面的例子。你不需要在你所有脚本定义里都使用这些接口。
* 参数 3 - 脚本扩展的 Java 类。扩展的类并不总是必要,这取决于脚本引擎的实现。
示例 classpath 位置:物理磁盘里的 "oflaDemo" 依赖的应用等同于 webapps/oflaDemo/WEB-INF/applications。
III. 创建一个应用脚本
1.应用适配器
一些语言中,使用脚本编写应用程序适配器比在其他语言中要难得多,藉此我介绍 Ruby 的例子,它的效果很好并易于编写和整合。这个应用服务在其他支持的语言中也很容易编写,但它们至少需要 Java 接口。
i. JRuby 应用适配器实现
# JRuby
require 'java'
module RedFive
include_package "org.red5.server.api"
include_package "org.red5.server.api.stream"
include_package "org.red5.server.api.stream.support"
include_package "org.red5.server.adapter"
include_package "org.red5.server.stream"
end
#
# application.rb - a translation into Ruby of the ofla demo application, a red5 example.
#
# @author Paul Gregoire
#
class Application < RedFive::ApplicationAdapter
attr_reader :appScope, :serverStream
attr_writer :appScope, :serverStream
def initialize
#call super to init the superclass, in this case a Java class
super
puts "Initializing ruby application"
end
def appStart(app)
puts "Ruby appStart"
@appScope = app
return true
end
def appConnect(conn, params)
puts "Ruby appConnect"
measureBandwidth(conn)
puts "Ruby appConnect 2"
if conn.instance_of?(RedFive::IStreamCapableConnection)
puts "Got stream capable connection"
sbc = RedFive::SimpleBandwidthConfigure.new
sbc.setMaxBurst(8388608)
sbc.setBurst(8388608)
sbc.setOverallBandwidth(8388608)
conn.setBandwidthConfigure(sbc)
end
return super
end
def appDisconnect(conn)
puts "Ruby appDisconnect"
if appScope == conn.getScope && @serverStream != nil
@serverStream.close
end
super
end
def toString
return "Ruby toString"
end
def setScriptContext(scriptContext)
puts "Ruby application setScriptContext"
end
def method_missing(m, *args)
super unless @value.respond_to?(m)
return @value.send(m, *args)
end
end
2.应用服务
这里是一个 Java 接口的示例(是的,假设方法是空的),这个接口用于为搜集一系列的文件并将它们以 Map(名-值 对)返回给调用者的应用提供一个模板。
i. 由脚本执行简单的 Java 接口
package org.red5.server.webapp.oflaDemo;
import java.util.Map;
public interface IDemoService {
/**
* Getter for property 'listOfAvailableFLVs'.
*
* @return Value for property 'listOfAvailableFLVs'.
*/
public Map getListOfAvailableFLVs();
public Map getListOfAvailableFLVs(String string);
}
ii. Spring bean 关于脚本实现的接口的定义
<bean id="demoService.service" class="org.springframework.scripting.jruby.JRubyScriptFactory">
<constructor-arg index="0" value="classpath:applications/demoservice.rb"/>
<constructor-arg index="1">
<list>
<value>org.red5.server.webapp.oflaDemo.IDemoService</value>
</list>
</constructor-arg>
</bean>
iii. JRuby 脚本实现接口
# JRuby - style
require 'java'
module RedFive
include_package "org.springframework.core.io"
include_package "org.red5.server.webapp.oflaDemo"
end
include_class "org.red5.server.api.Red5"
include_class "java.util.HashMap"
#
# demoservice.rb - a translation into Ruby of the ofla demo application, a red5 example.
#
# @author Paul Gregoire
#
class DemoService < RedFive::DemoServiceImpl
attr_reader :filesMap
attr_writer :filesMap
def initialize
puts "Initializing ruby demoservice"
super
@filesMap = HashMap.new
end
def getListOfAvailableFLVs
puts "Getting the FLV files"
begin
dirname = File.expand_path('webapps/oflaDemo/streams').to_s
Dir.open(dirname).entries.grep(/\.flv$/) do |dir|
dir.each do |flvName|
fileInfo = HashMap.new
stats = File.stat(dirname+'/'+flvName)
fileInfo["name"] = flvName
fileInfo["lastModified"] = stats.mtime
fileInfo["size"] = stats.size || 0
@filesMap[flvName] = fileInfo
print 'FLV Name:', flvName
print 'Last modified date:', stats.mtime
print 'Size:', stats.size || 0
print '-------'
end
end
rescue Exception => ex
puts "Error in getListOfAvailableFLVs #{errorType} \n"
puts "Exception: #{ex} \n"
puts caller.join("\n");
end
return filesMap
end
def formatDate(date)
return date.strftime("%d/%m/%Y %I:%M:%S")
end
def method_missing(m, *args)
super unless @value.respond_to?(m)
return @value.send(m, *args)
end
end
iv. Java 应用实现接口,基于 Ruby 代码(在不需要使用脚本时的代码)
package org.red5.server.webapp.oflaDemo;
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;
public class DemoService {
protected static Log log = LogFactory.getLog(DemoService.class.getName());
/**
* Getter for property 'listOfAvailableFLVs'.
Scripting Implementations
*
* @return Value for property 'listOfAvailableFLVs'.
*/
public Map getListOfAvailableFLVs() {
IScope scope = Red5.getConnectionLocal().getScope();
Map<String, Map> filesMap = new HashMap<String, Map>();
Map<String, Object> fileInfo;
try {
log.debug("getting the FLV files");
Resource[] flvs = scope.getResources("streams/*.flv");
if (flvs != null) {
for (Resource flv : flvs) {
File file = flv.getFile();
Date lastModifiedDate = new Date(file.lastModified());
String lastModified = formatDate(lastModifiedDate);
String flvName = flv.getFile().getName();
String flvBytes = Long.toString(file.length());
if (log.isDebugEnabled()) {
log.debug("flvName: " + flvName);
log.debug("lastModified date: " + lastModified);
log.debug("flvBytes: " + flvBytes);
log.debug("-------");
}
fileInfo = new HashMap<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 (Resource mp3 : mp3s) {
File file = mp3.getFile();
Date lastModifiedDate = new Date(file.lastModified());
String lastModified = formatDate(lastModifiedDate);
String flvName = mp3.getFile().getName();
String flvBytes = Long.toString(file.length());
if (log.isDebugEnabled()) {
log.debug("flvName: " + flvName);
log.debug("lastModified date: " + lastModified);
log.debug("flvBytes: " + flvBytes);
log.debug("-------");
}
fileInfo = new HashMap<String, Object>();
fileInfo.put("name", flvName);
fileInfo.put("lastModified", lastModified);
fileInfo.put("size", flvBytes);
filesMap.put(flvName, fileInfo);
}
}
} catch (IOException e) {
log.error(e);
}
return filesMap;
}
private String formatDate(Date date) {
SimpleDateFormat formatter;
String pattern = "dd/MM/yy H:mm:ss";
Locale locale = new Locale("en", "US");
formatter = new SimpleDateFormat(pattern, locale);
return formatter.format(date);
}
}
a.Flex AS3 方法调用服务
[Bindable]
public var videoList:ArrayCollection;
public function catchVideos():void{
// call server-side method
// create a responder and set it to getMediaList
var nc_responder:Responder = new Responder(getMediaList, null);
// call the server side method to get list of FLV's
nc.call("demoService.getListOfAvailableFLVs", nc_responder);
}
public function getMediaList(list:Object):void{
// this is the result of the server side getListOfAvailableFLVs
var mediaList:Array = new Array();
for(var items:String in list){
mediaList.push({label:items, size:list[items].size, dateModified:list[items].lastModifi
}
// videoList is bindable and the datagrid is set to use this for it's dataprovider
// wrap it in an ArrayCollection first
videoList = new ArrayCollection(mediaList);
}
IV. 创建你自己的解释器
级别:高级
在打开这个话题之前先提一下上周末 02/2007 试图建立一个PHP解释器,那阵势一个痛苦的过程哈,四个小时之后我不得不放弃。这件事我汲取的教训就是你首先得断定你的脚本语言可以作为应用程序运行,而不是作为 HTTP 请求处理器。这里可以先测试一下:X 语言是否可以编译成一个可执行程序,或者它可以使用命令行运行?如果可以的话,那它就可以进行轻易整合。
V. 脚本信息的相关链接
* Spring 脚本
http://static.springframework.org/spring/docs/2.0.x/reference/dynamic-language.html
http://rhinoinspring.sourceforge.net/
* Java 脚本
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/
http://blogs.sun.com/sundararajan/
https://scripting.dev.java.net/
http://today.java.net/pub/a/today/2006/04/11/scripting-for-java-platform.html
http://www.javaworld.com/javaworld/jw-03-2005/jw-0314-scripting_p.html
http://www.oreillynet.com/onjava/blog/2004/01/java_scripting_half_the_size_h.html
http://www.robert-tolksdorf.de/vmlanguages.html
* Javascript
http://www.mozilla.org/rhino/
http://www.mozilla.org/rhino/ScriptingJava.html
* Ruby
http://jruby.codehaus.org/
* BeanShell
http://www.beanshell.org/
* Python
http://www.jython.org/Project/
http://www.onjava.com/pub/a/onjava/2002/03/27/jython.html
http://jepp.sourceforge.net/
http://jpe.sourceforge.net/
http://jpype.sourceforge.net/
* Groovy
http://groovy.codehaus.org/
原文链接: http://trac.red5.org/wiki/Documentation/UsersReferenceManual/Red5CoreTechnologies/05-Scripting-Implementations。