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


    如果不考虑安全因素(指任何人都可连接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

    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    springmvc 拦截器
    springmvc 文件上传
    springmvc 数据验证 hibernate-validator --->对象验证
    springmvc 类型转换器 数据回显及提示信息
    springmvc框架自带的异常处理器SimpleMappingExceptionResolver的使用
    如何解决JSP页面顶端报错 The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path
    eval函数的使用之一
    【模板】负环判定
    【洛谷P1072】Hankson 的趣味题
    【洛谷P1463】反素数
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/1817865.html
Copyright © 2020-2023  润新知