如果不考虑安全因素(指任何人都可连接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; } } }
界面:
示例源代码下载:http://cid-2959920b8267aaca.office.live.com/self.aspx/Flash/VideoRecording.rar