AdobeAIRでWebカメラの映像を録画して保存


Sample ApplicationAdobe AIR を使って、Webカメラの映像をキャプチャして動画ファイルで保存できるサンプルアプリを作ってみました。

カメラから取り込んだ動画のエンコード・保存には、FMS などのサーバは一切使用せずに、このアプリ内の機能のみで動画をエンコードしてファイル( .flv )として保存できるようにしてみました。

今回は、【映像の取り込み 〜 エンコード 〜 保存 〜 保存した映像を再生】という、一通りの動作が確認できるものを作ってみました。
次はこれに音声の取り込み・エンコードもできるようにしてみようかと思っています。

今回作ったサンプルアプリのダウンロード( Adobe AIR 3.9 以降が必要です )は
こちらから

今回映像のエンコードに使用したライブラリ( leelib.util.flvEncoder )は
こちらを使用させていただきました

このライブラリを使用すれば、今回サンプルで使用した機能は簡単に実装できますので、興味がある方はお試しください。

以下、今回のテストコードです。

package
{
	import com.bit101.components.PushButton;
	import com.bit101.components.TextArea;

	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.NetStatusEvent;
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.media.Camera;
	import flash.media.Video;
	import flash.net.NetConnection;
	import flash.net.NetStream;

	import leelib.util.flvEncoder.FileStreamFlvEncoder;
	import leelib.util.flvEncoder.VideoPayloadMakerAlchemy;


	[SWF( width = '640', height = '480', frameRate = '60', backgroundColor = '0' )]
	public class RecordTest extends Sprite
	{

		private var _flvWritter:FileStreamFlvEncoder;

		private var _cam:Camera;
		private var _video:Video;
		private var _buffer:BitmapData;

		private var _ns:NetStream;
		private var _nc:NetConnection;

		private var _file:File

		private var _recBtn:PushButton;
		private var _stopBtn:PushButton;
		private var _removedFileBtn:PushButton;
		private var _msg:TextArea;

		private var _videoFilePath:String;


		public function RecordTest()
		{
			setup();
		}


		private function setup():void
		{
			_video = new Video( 640, 480 );
			addChild( _video );

			var ctrlArea:Shape = new Shape();
			var g:Graphics = ctrlArea.graphics;
			g.beginFill( 0x000810, .6 );
			g.drawRect( 0, 420, 640, 60 );
			addChild( ctrlArea );

			_recBtn = new PushButton( this, 153, 442, "Rec Start", onRecStart );
			_stopBtn = new PushButton( this, 260, 442, "Rec Stop", onRecStop );
			_removedFileBtn = new PushButton( this, 10, 442, "VideoFile Removed", onFileRemoved );
			_msg = new TextArea( this, 390, 430 );

			_stopBtn.enabled = false;
			_removedFileBtn.enabled = false;
			_msg.width = 240;
			_msg.height = 40;

			_nc = new NetConnection();
			_nc.connect( null );
			_ns = new NetStream( _nc );

			var metaSniffer:Object = new Object();
			metaSniffer.onMetaData = onMetaData;
			_ns.client = metaSniffer;

			function onMetaData( meta:Object ):void
			{
				trace( " (´`c_,'`)プッ  ( ^∀^)ゲラゲラ ゲラゲラ" );
			}
		}


		private function onRecStart( e:MouseEvent ):void
		{
			_video.visible = true;
			_recBtn.enabled = false;
			_stopBtn.enabled = true;

			var fps:int = 15;
			stage.frameRate = fps;

			_cam = Camera.getCamera();
			_cam.setMode( 640, 480, fps );
			_video.attachCamera( _cam );

			_buffer = new BitmapData( _cam.width, _cam.height );

			_file = File.desktopDirectory.resolvePath( "capturedVideo" + ".flv" );
			_videoFilePath = _file.url;

			_flvWritter = new FileStreamFlvEncoder( _file, fps );
			_flvWritter.fileStream.openAsync( _file, FileMode.UPDATE );
			_flvWritter.setVideoProperties( 640, 480, VideoPayloadMakerAlchemy );

			_flvWritter.start();
			addEventListener( Event.ENTER_FRAME, recUpdate );

			_msg.text = "Now Recording...\n" + _videoFilePath;
		}


		private function onRecStop( e:MouseEvent ):void
		{
			_stopBtn.enabled = false;

			removeEventListener( Event.ENTER_FRAME, recUpdate );

			_flvWritter.updateDurationMetadata();
			_flvWritter.fileStream.close();
			_flvWritter.kill();
			_buffer.dispose();

			_video.attachCamera( null );
			_cam = null;

			stage.frameRate = 60;

			videoPlayBack();
		}


		private function recUpdate( e:Event ):void
		{
			_cam.drawToBitmapData( _buffer );
			_flvWritter.addFrame( _buffer, null );
		}


		private function onFileRemoved( e:MouseEvent ):void
		{
			if ( _file )
			{
				_file.deleteFile();
				_file = null;

				_msg.text = "Video File Removed.";
				_removedFileBtn.enabled = false;
			}
		}


		private function videoPlayBack():void
		{
			_msg.text = "Video Replay Now.\n" + _videoFilePath;

			_ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );

			_video.attachNetStream( _ns );
			_ns.play( _videoFilePath );
		}


		private function onNetStatus( e:NetStatusEvent ):void
		{
			trace( e.info.code );

			if ( e.info.code == "NetStream.Play.Stop" )
			{
				_ns.removeEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
				_nc.close();

				_msg.text = "Video Stoped.\n" + _videoFilePath;

				_video.visible = false;
				_recBtn.enabled = true;
				_stopBtn.enabled = false;
				_removedFileBtn.enabled = true;
			}
		}
	}
}