以Action Script 3.0(简称AS)开发Browser Player时,需要用NetStream,但现在NetStream.play只支持Http和File两种协议。上网检查youtube,tudou和youku,发现他们用的播放协议也都是Http。而以Flash Media Server(简称FMS)或Red5作为流媒体服务器时,它们提供的是RTMP协议,且这两种流媒体服务器是专门做过优化的。这两种协议,HTTP和RTMP,有几点不同:
(1)用HTTP方式: 先通过IIS 将FLV下载到本地缓存,然后再通过NetConnection的本地连接来播放这个FLV,这种方法是播放本地的视频,并不是播放服务器的视频。因此在本地缓存里可以找到这个FLV。其优点就是服务器下载完这个FLV,服务器就没有消耗了,节省服务器消耗。其缺点就是FLV会缓存在客户端,对FLV的保密性不好。
(2)用RTMP方式: 通过NetConnection连接到FMS/Red5服务器,并实时播放服务器的FLV文件,这种方式可以任意选择视频播放点,并不象HTTP方式需要缓存完整个FLV文件到本地才可以任意选择播放点,其优点就是在本地缓存里是找不到这个FLV文件的。其优点就是FLV不会缓存在客户端,FLV的保密性好,其缺点就是消耗服务器资源,连接始终是实时的。
由以上分析可以知道,Http方式是本地播放,而RTMP方式是服务器实时播放。
二、 AS介绍
AS 自3.0开始采用面向对象的设计,而在运行速度上也有了很大的提高。在面向对象的基础上,AS也采纳了类C#和Java的Package,namespace逻辑组织结构,更方便于RIA的开发。
AS通常可以在3种环境下进行开发。
(1) 文本编辑器和编译器。
安装AS的编译器,利用记事本等文本编辑器进行开发,保存为AS文件后进行编译即可。通常可以利用Ant组织编译生成swf。
(2) 利用Adobe Flash CS 3进行开发
Adobe将Macromedia并入旗下后,便推出了不同于以往Macromedia Flash 8.0版本号的Adobe Flash CS 3。在Flash CS3中,用户可以方便的将设计FLASH场景、图层等同编写AS脚本相结合。同时,由于Flash CS3内置了很多Flash控件,用户可以将通过fl.control这个开发包方便的使用这些控件。
(3) 利用Flex Builder 3进行开发
这是一种基于Eclipse的IDE,熟悉Eclipse的用户可以像编写Java程序一样来编写AS。同时Flex Builder 3的主要功能是为基于Flex框架程序提供了良好的开发环境。若需要在Flex Builder 3中AS工程中使用内置的控件,首先需要将Flash CS3中的标准库导入到场景中,然后发布成为swc文件。最后将此swc文件导入到AS的编译路径中即可。
三、基于AS的Flv Player
(1) 自行编写Flv Player
编写Flv Player仅需要对AS相关NetStream操作部分有些了解, HTML和JS的基础知识。下面是一个基本的范例:
package {
import fl.controls.Button;
import flash.display.Sprite;
import flash.events.*;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.text.TextField;
public class Player extends Sprite
{
private var _width:int;
private var _height:int;
private var _stream:NetStream;
private var _connection:NetConnection;
private var _video:Video;
private var _playbackTime:TextField = new TextField();
private var _duration:uint;
private var _info:TextField = new TextField();
private var btStart:Button = new Button();
private var btStop:Button = new Button();
private var btPause:Button = new Button();
private var movie:String = "";
private var status:Boolean = true;
public function Player()
{
initialize();
var param:Object = root.loaderInfo.parameters;
movie = String(param["movie"]);
_info.y = 360;
this.addChild(_info);
}
private function initialize():void{
_connection = new NetConnection();
_connection.connect(null);
_stream = new NetStream(_connection);
_stream.addEventListener(NetStatusEvent.NET_STATUS, onStatus);
_stream.bufferTime = 10;
_video = new Video();
this.addChild(_video);
_video.attachNetStream(_stream);
var client:Object = new Object( );
client.onMetaData = onMetaData;
_stream.client = client;
_playbackTime.y = 300;
addChild(_playbackTime);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
btStart.label = "start";
btStart.y = 300;
btStart.x = 0;
btStart.addEventListener(MouseEvent.CLICK, startButtonAction);
addChild(btStart);
btStop.label = "stop";
btStop.y = 300;
btStop.x = 120;
btStop.addEventListener(MouseEvent.CLICK, stopButtonAction);
addChild(btStop);
btPause.label = "pause";
btPause.y = 300;
btPause.x = 240;
btPause.addEventListener(MouseEvent.CLICK, pauseButtonAction);
addChild(btPause);
}
private function startButtonAction(e:MouseEvent):void{
_stream.play(movie);
}
private function stopButtonAction(e:MouseEvent):void{
_stream.close();
}
private function pauseButtonAction(e:MouseEvent):void{
if(status){
_stream.pause();
status = false;
}else{
_stream.resume();
status = true;
}
}
private function onMetaData(data:Object):void {
_duration = data.duration;
}
private function onEnterFrame(event:Event):void {
if(_duration > 0 && _stream.time > 0) {
_playbackTime.text = Math.round(_stream.time) + " / " +
Math.round(_duration);
}
}
private function onStatus(event:NetStatusEvent):void {
if(_video.videoWidth > 0 && _video.width != _video.videoWidth) {
_width = _video.videoWidth;
_height = _video.videoHeight;
var param:Object = root.loaderInfo.parameters;
var widthString:String = String(param["width"]);
var heightString:String = String(param["height"]);
var width:int = -1;
var height:int = -1;
if(widthString != "undefined"){
width = int(widthString);
}
if(heightString != "undefined"){
height = int(heightString);
}
if(width == -1){
if(height == -1){
_video.width = _width;
_video.height = _height;
}else{
_video.height = height;
_video.width = _width * height / _height;
}
}else{
_video.width = width;
_video.height = _height * width / _width;
}
}
}
}
}
(2) 使用开源的Flv player
推荐使用Flowplayer,可以下载它的源代码,同样是利用ant进行编译。但是需要注意的是,需要进行简单的修改。
在build.properties文件中修改MTASC_BIN和SWFMILL_BIN的值
//MTASC_BIN=/opt/mtasc/mtasc
MTASC_BIN=bin/mtasc/mtasc.exe
// You should use swfmill version 0.2.11 (will not work with the newer version)
//SWFMILL_BIN=/usr/local/bin/swfmill
SWFMILL_BIN=bin/swfmill/swfmill.exe
在skinbuild.xml文件中
<target name="set-os-swfmill-bin">
<condition property="os_swfmill_bin"
value="${SWFMILL_BIN}"
else="${SWFMILL_BIN}">
<!--原来是value="${SWFMILL_BIN_WIN}" else="${SWFMILL_BIN}" -->
<os family="windows"/>
</condition>
</target>
将更改后,执行ant即可,就可以得到重新编译好的flowplayer。
当需要更改播放器皮肤,特别是需要将播放器皮肤提示信息的英文改为中文,即修改skin.as部分代码,这时候就必须要重新编译了。
四、支持RTMP协议的Red5 Server
Red5是Java实现的一种开源的Flash流媒体服务器,它支持FLV和MP3格式的Streaming Audio/Video、FLV格式的Recording Client Stream、共享对象、实时流发布等多种功能。
当使用Red5作为Server时,需要完成客户端和服务器端两部分的工作。在服务器端,写自己的应用程序,即rtmp服务的提供。这个应用程序的入口应该extends ApplicationAdapter这个类,然后将它部署到Server上。服务器端示例代码如下:
import org.red5.server.adapter.ApplicationAdapter;
public class Application extends ApplicationAdapter {
public Double add(Double a, Double b){
return a + b;
}
}
在客户端由AS完成,去访问服务器端构造的服务,示例代码如下
nc = new NetConnection();
nc.connect("rtmp://localhost/myapp");
nc.onResult = function(obj) {
trace("The result is " + obj);
}
nc.call("add", nc, 1, 2);
这样就完成了基本的实现了 AS 通过 RTMP 协议调用 Red5 服务器端所提供的服务。更多的具体内容,可以参考 Red5 文档去实践。