如果不考虑安全因素(指任何人都可连接FluorineFx进行视频录制,而不需要登录认证),其实服务端不用写一行代码,仅需要在apps目录下建一个子目录当作应用,以及在services-config.xml中配置一下rtmp的Channel即可

 

下面这段flash客户端的as3代码,是从FluorineFx官方的Flash AS2示例修改而来的(当然:只一个示例,细节还有很多可优化的地方)



package 
{
import fl.controls.Button;
import fl.controls.Label;
import fl.controls.TextInput;
import fl.controls.CheckBox;
import flash.display.Sprite;
import flash.utils.Timer;
import flash.events.ActivityEvent;
import flash.events.TimerEvent;
import flash.events.MouseEvent;
import flash.events.NetStatusEvent;
import flash.events.StatusEvent;
import flash.media.Camera;
import flash.media.Microphone;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.media.Video;


public class VideoRecord extends Sprite
{
private var _btnRecord:Button;
private var _btnPlay:Button;
private var _btnConnect:Button;
private var _txtVideoFileName:TextInput;
private var _chkAppend:CheckBox;
private var _txtServerUrl:TextInput;
private var _lblResult:Label;
private var _nc:NetConnection = null;
private var _nsPublish:NetStream = null;
private var _nsPlay:NetStream = null;
private var _ncPlay:NetConnection = null;
private var _camera:Camera;
private var _microphone:Microphone;
private var _videoRecord:Video;
private var _videoPlay:Video;
private var _videoIsWorked = false;
private var _timer:Timer;


public function VideoRecord()
{
init();
}

private function init():void
{

this._btnRecord = this.btnRecord;
this._txtVideoFileName = this.videoFileName;
this._chkAppend = this.chk1;
this._chkAppend.label = "追加";
this._btnPlay = btnPlay;
this._btnConnect = btnConnect;
this._txtServerUrl = this.txtServerUrl;
this._lblResult = lblResult;
this._btnRecord.enabled = false;
this._btnRecord.label = "录制";
this._txtVideoFileName.enabled = false;
this._chkAppend.enabled = false;
this._btnPlay.enabled = false;
this._btnPlay.label = "播放";
this._btnConnect.label = "连接";
this._txtServerUrl.text = "rtmp://localhost/VideoRecording";
this._videoRecord = videoRecord;
this._videoPlay = videoPlay;
this._btnConnect.addEventListener(MouseEvent.CLICK, doConnect);
}

//连接服务器;
private function doConnect(e:MouseEvent):void
{

if (this._nc == null)
{
this._nc = new NetConnection ;
this._nc.addEventListener(NetStatusEvent.NET_STATUS, ncNetStatus);
}
this._nc.connect(this._txtServerUrl.text);
this._lblResult.text = "服务器连接中...";
}

//关闭与服务器的连接
private function doCloseConn(e:MouseEvent):void
{
if (this._nc != null)
{
if (this._nsPublish != null)
{
this._nsPublish.attachCamera(null);
}
this._videoRecord.attachCamera(null);
this._videoRecord.attachNetStream(null);
this._videoRecord.clear();
this._nc.close();
this._btnConnect.label = "连接";
this._btnRecord.enabled = false;
this._txtVideoFileName.enabled = false;
this._chkAppend.enabled = false;
this._videoRecord.clear();

this._btnConnect.removeEventListener(MouseEvent.CLICK,doCloseConn);
this._btnConnect.addEventListener(MouseEvent.CLICK,doConnect );

stopPublish();
}
}

//检测conn对象的状态变化
private function ncNetStatus(e:NetStatusEvent):void
{
//trace(e.info.code);
if (e.info.code == "NetConnection.Connect.Success")
{
//连接成功
this._lblResult.text = "服务器已经连接!";
this._btnConnect.label = "断开";
this._btnConnect.removeEventListener(MouseEvent.CLICK,doConnect);
this._btnConnect.addEventListener(MouseEvent.CLICK, doCloseConn);
this._camera = Camera.getCamera();
if (_camera == null)
{
this._lblResult.text = "未安装摄像头!";
return;
}

_camera.addEventListener(StatusEvent.STATUS, cameraStatusHandler);
_camera.addEventListener(ActivityEvent.ACTIVITY, cameraActivityHandler);
this._videoRecord.attachCamera(this._camera);

//点击"断开"后后,又重新点击"连接";
if (_videoIsWorked)
{
//恢复控件的可用性;
this._txtVideoFileName.enabled = true;
this._chkAppend.enabled = true;
this._btnRecord.enabled = true;

if (this._txtVideoFileName.text == "")
{
//this._txtVideoFileName.text = Math.round(Math.random() * 10000).toString();
this._txtVideoFileName.text = "demo";
}
this._btnRecord.label = "录制";
this._btnRecord.removeEventListener(MouseEvent.CLICK,prepareStopRecord);
this._btnRecord.addEventListener(MouseEvent.CLICK, startRecord);
}
}
else if (e.info.code == "NetConnection.Connect.Closed")
{
this._lblResult.text = "服务器连接已关闭!";
}
else
{
this._lblResult.text = "错误-服务器连接失败!";
}
}

//用户选择是否摄像头时触发
function cameraStatusHandler(e:StatusEvent):void
{
//trace(e);
if (e.code == "Camera.Muted")
{
this._lblResult.text = "您不允许使用摄像头!";
}
else if (e.code == "Camera.Unmuted")
{
this._lblResult.text = "摄像头视频获取中...";
_timer = new Timer(100,20);//每隔100ms检测摄像头状态,一共检测20次
cameraActivityHandler(null);
}
}

//摄像头有活动时被触发
private function cameraActivityHandler(e:ActivityEvent):void
{
//trace("cameraActivityHandler被调用!");
if (! _videoIsWorked)
{
if (_timer != null)
{
_timer.addEventListener(TimerEvent.TIMER, checkCamera);
_timer.addEventListener(TimerEvent.TIMER_COMPLETE, checkCameraComplete);
_timer.start();
//trace("_timer已经启动!");
}
}
}


//timer回调函数,用于检测摄像头设备是否正确
function checkCamera(e:TimerEvent):void
{
this._lblResult.text = "摄像头视频获取中...";
if (this._camera.currentFPS > 0)
{
_timer.stop();
_videoIsWorked = true;
this._lblResult.text = "摄像头工作正常";

//恢复控件的可用性;
this._txtVideoFileName.enabled = true;
this._chkAppend.enabled = true;
this._btnRecord.enabled = true;

if (this._txtVideoFileName.text == "")
{
//this._txtVideoFileName.text = Math.round(Math.random() * 10000).toString();
this._txtVideoFileName.text = "demo";
}
this._btnRecord.addEventListener(MouseEvent.CLICK, startRecord);
}
}

//开始录制
private function startRecord(e:MouseEvent):void
{
//trace("开始录制,_nsPublish=",_nsPublish);
if (this._nsPublish == null)
{
//trace("重新创建ns");
_nsPublish = new NetStream(this._nc);
}
this._nsPublish.attachCamera(this._camera);
this._nsPublish.publish(this._txtVideoFileName.text, this._chkAppend.selected ? "append" : "record");
this._nsPublish.addEventListener(NetStatusEvent.NET_STATUS, nsPublishNetStatus);
//缓冲20秒;
this._nsPublish.bufferTime = 20;



}

private function nsPublishNetStatus(e:NetStatusEvent):void
{
//trace(e.info.code);
if (e.info.code == "NetStream.Play.StreamNotFound" || e.info.code == "NetStream.Play.Failed" || e.info.code == "NetStream.Publish.BadName")
{
this._lblResult.text = "推送失败,原因:" + e.info.code;

}
else if (e.info.code == "NetStream.Record.Start" || e.info.code == "NetStream.Buffer.Empty")
{
//录制开始
this._btnRecord.removeEventListener(MouseEvent.CLICK, startRecord);
this._btnRecord.addEventListener(MouseEvent.CLICK, prepareStopRecord);
this._lblResult.text = "正在录制...";
this._btnRecord.label = "停止";

//录制时,禁止回放
this._btnPlay.enabled = false;
//this._btnPlay.label = "播放";
this._btnPlay.removeEventListener(MouseEvent.CLICK,this.doStopPlay);
this._btnPlay.addEventListener(MouseEvent.CLICK,this.doPlay);
}
}

private function nsPlayNetStatus(e:NetStatusEvent):void
{
//trace(e.info.code);
//失败
if (e.info.code == "NetStream.Play.StreamNotFound" || e.info.code == "NetStream.Play.Failed")
{

}
else if (e.info.code=="NetStream.Play.Start")
{

}
}

private function doStopPlay(e:MouseEvent):void
{
if (this._nsPlay != null)
{
this._videoPlay.attachNetStream(null);
this._videoPlay.clear();
this._ncPlay.close();
this._ncPlay = null;
this._nsPlay.close();
this._nsPlay = null;

this._btnPlay.label = "播放";
this._btnPlay.removeEventListener(MouseEvent.CLICK,doStopPlay);
this._btnPlay.addEventListener(MouseEvent.CLICK,doPlay);
//trace("已经停止!");
}

}


function doPlay(e:MouseEvent):void
{
if (this._ncPlay == null)
{
this._ncPlay = new NetConnection();
this._ncPlay.addEventListener(NetStatusEvent.NET_STATUS,ncPlayNetStatus);
this._ncPlay.connect(this._txtServerUrl.text);
}
}


private function ncPlayNetStatus(e:NetStatusEvent):void
{
if (e.info.code == "NetConnection.Connect.Success")
{
if (this._nsPlay == null)
{
//trace("_nsPlay已经创建!");
this._nsPlay = new NetStream(this._ncPlay);
this._nsPlay.addEventListener(NetStatusEvent.NET_STATUS, nsPlayNetStatus);

var _client:Object = new Object();
_client.onMetaData = nsPlayOnMetaData;
_client.onPlayStatus = nsPlayOnPlayStatus;
this._nsPlay.client = _client;
this._nsPlay.bufferTime = 5;

this._nsPlay.play(this._txtVideoFileName.text);
this._videoPlay.attachNetStream(this._nsPlay);
this._btnPlay.label = "停止";
this._btnPlay.removeEventListener(MouseEvent.CLICK,doPlay);
this._btnPlay.addEventListener(MouseEvent.CLICK,doStopPlay);
}

}
}

private function nsPlayOnMetaData(e:Object):void
{
trace("onmetaData:" + e.duration);
}

private function nsPlayOnPlayStatus(e:Object):void
{
trace("onPlayStatus:" + e.code);

if (e.code == "NetStream.Play.Complete")
{
trace("播放已经停止");
this._videoPlay.attachNetStream(null);
this._videoPlay.clear();
this._btnPlay.label = "播放";
this._btnPlay.removeEventListener(MouseEvent.CLICK,doStopPlay);
this._btnPlay.addEventListener(MouseEvent.CLICK,doPlay);

this._ncPlay.close();
this._ncPlay = null;
this._nsPlay.close();
this._nsPlay = null;
}
}

//准备停止录制
private function prepareStopRecord(e:MouseEvent)
{

this._nsPublish.attachCamera(null);
var _bufferLength = this._nsPublish.bufferLength;
//必须等当前缓冲区中的数据全部发送完以后再正式停止
if (_bufferLength > 0)
{
this._btnRecord.label = "稍等...";
this._lblResult.text = "正在保存,请稍候...";
//每0.1秒检查一次
_timer = new Timer(100);
_timer.addEventListener(TimerEvent.TIMER, doWait);
_timer.start();
}
else
{
//trace("停止!");
stopPublish();
}
}

//停止发布(录制)
private function stopPublish():void
{
if (this._nsPublish != null)
{
this._nsPublish.removeEventListener(NetStatusEvent.NET_STATUS, nsPublishNetStatus);
this._nsPublish.close();
this._nsPublish = null;
}
this._btnRecord.label = "录制";
this._lblResult.text = "";
this._btnRecord.removeEventListener(MouseEvent.CLICK, prepareStopRecord);
this._btnRecord.addEventListener(MouseEvent.CLICK, startRecord);


//允许回放;
this._btnPlay.enabled = true;
this._btnPlay.label = "播放";
this._btnPlay.removeEventListener(MouseEvent.CLICK,doStopPlay);
this._btnPlay.addEventListener(MouseEvent.CLICK,doPlay);
}



//等待录制视频缓冲区的数据全部保存到服务器上;
private function doWait(e:TimerEvent)
{
var _bufferLength = this._nsPublish.bufferLength;
if (_bufferLength <= 0)
{
_timer.removeEventListener(TimerEvent.TIMER, doWait);
_timer.stop();
_timer = null;
stopPublish();
}
}

function checkCameraComplete(e:TimerEvent):void
{
this._lblResult.text = "设备无法使用(有可能被占用)";
_timer.removeEventListener(TimerEvent.TIMER, checkCamera);
_timer.removeEventListener(TimerEvent.TIMER_COMPLETE, checkCameraComplete);
_timer = null;
}

}
}


 

界面:

FluorineFx:视频录制及回放(Flash/AS3环境)_fluorineFx

 

 

作者:菩提树下的杨过