Flex + Red5で動画保存
Red5を用いてWebカメラの動画を保存してみました。
サーバーにFLVとして保存できるので、ffmpegやOpenCVと組み合わせると面白いことができると思います。
動作ムービーはこちら
左の映像がカメラキャプチャで右が保存されたものをストリーミングで再生しています。
http://jp.youtube.com/watch?v=ns3rcoGUS10
作成方法の簡単な説明です。
まずはRed5をダウンロード+インストールします。
http://www.osflash.org/red5
Red5をインストールすることによりFlash上の動画をサーバーにFLVとして保存することができるようになります。
Flexのソースはこちら。
別途、com.renaun.samples.net.FMSConnection が必要です。
FLVファイルは「C:\Program Files\Red5\webapps\oflaDemo\streams」に保存されます。
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal" creationComplete="init()"> <mx:Script> <![CDATA[ import mx.core.UIComponent; import com.renaun.samples.net.FMSConnection; private var nc:FMSConnection; private var clientID:Number; private var ns:NetStream; private var lastVideoName:String; private var camera:Camera; //初期化関数 private function init():void{ //Netコネクション作成 nsConnect(); // ボタンの設定 btnStart.enabled = true; btnStop.enabled = false; //カメラの設定 camera = Camera.getCamera(); if (camera != null) { camera.setQuality(0, 95); camera.setMode(400 , 300 , 15 , true ); camera.addEventListener(ActivityEvent.ACTIVITY, activityHandler); myFlv.attachCamera( camera ); myLog.text = "get camera "; } else { trace("You need a camera."); myLog.text = "not found camera "; } } // Netコネクション作成 private function nsConnect():void{ NetConnection.defaultObjectEncoding = flash.net.ObjectEncoding.AMF0; nc = new FMSConnection(); nc.addEventListener( "success", connectionSuccessHandler ); nc.connect( "rtmp://localhost/oflaDemo" ); } private function connectionSuccessHandler( event:Event ):void { clientID = nc.clientID; } //録画開始 private function startClick():void{ myLog.text = "start"; // ネットストリーム作成 ns = new NetStream(nc); //メタデータの設定(なんかないと怒られる) var client:Object = new Object(); client.onMetaData = onMetaData; ns.client = client // マイクの設定 var mic:Microphone = Microphone.getMicrophone(); ns.attachCamera(camera); ns.attachAudio(mic); //ファイル名 lastVideoName = "red5RecordDemo_" + Math.random(); //録音スタート ns.publish(lastVideoName, "record"); // ボタンの設定 btnStart.enabled = false; btnStop.enabled = true; } private function activityHandler( e:ActivityEvent ):void{ } //録音停止 private function stopClick():void{ myLog.text = "stop"; ns.close(); //録音したものを再生(なんかめんどっちい var uiC:UIComponent = new UIComponent(); uiC.width = 400; uiC.height = 300; var video:Video = new Video(400,300); video.attachNetStream( ns ); video.width = 400; video.height = 300; uiC.addChild(video); videoContainer.addChild(uiC); ns.play(lastVideoName + ".flv"); // ボタンの設定 btnStart.enabled = true; btnStop.enabled = false; } // メタデータ取得 private function onMetaData(data:Object):void{ } private function onPlay():void{ ns.play(lastVideoName + ".flv"); } ]]> </mx:Script> <mx:Panel width="208" height="391" layout="absolute"> <mx:TextArea x="10" y="38" width="167" height="197" id="myLog"/> <mx:Button id="btnStop" x="116" y="10" label="ストップ" click="stopClick()"/> <mx:Button id="btnStart" x="45" y="10" label="スタート" click="startClick()"/> <mx:Label x="10" y="12" text="録音"/> </mx:Panel> <mx:Panel width="842" height="390" layout="absolute"> <mx:Grid x="10" y="10"> <mx:GridRow width="100%" height="100%"> <mx:GridItem width="100%" height="100%"> <mx:Label text="カメラ動画"/> </mx:GridItem> <mx:GridItem width="100%" height="100%"> <mx:Label text="FLV保存された動画"/> <mx:Button label="再生" click="onPlay()"/> </mx:GridItem> </mx:GridRow> <mx:GridRow width="100%" height="100%"> <mx:GridItem width="100%" height="100%"> <mx:VideoDisplay width="400" height="300" id="myFlv"/> </mx:GridItem> <mx:GridItem width="100%" height="100%"> <mx:Canvas id="videoContainer" width="400" height="300"> </mx:Canvas> </mx:GridItem> </mx:GridRow> <mx:GridRow width="100%" height="100%"> </mx:GridRow> </mx:Grid> </mx:Panel> </mx:Application>
com.renaun.samples.net.FMSConnection のソース
package com.renaun.samples.net { import flash.net.NetConnection; import flash.net.SharedObject; import flash.events.NetStatusEvent; import flash.events.SecurityErrorEvent; import flash.events.AsyncErrorEvent; import flash.events.IOErrorEvent import flash.events.Event; import flash.events.IEventDispatcher; [Event(name="success", type="flash.events.Event")] [Event(name="failed", type="flash.events.Event")] /** * Note: This class was dynamic in ActionScript 2.0 but is now sealed. * To write callback methods for this class, you can either extend the * class and define the callback methods in your subclass, or you can * use the client property to refer to an object and define the callback * methods on that object. */ dynamic public class FMSConnection extends NetConnection implements IEventDispatcher { //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor */ public function FMSConnection() { super(); } public var clientID:Number; //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * Connect */ override public function connect( url:String, ...args ):void { // Set object encoding to be compatible with Flash Media Server this.objectEncoding = flash.net.ObjectEncoding.AMF0; NetConnection.defaultObjectEncoding // Add status/security listeners this.addEventListener( NetStatusEvent.NET_STATUS, netStatusHandler ); this.addEventListener( SecurityErrorEvent.SECURITY_ERROR, netSecurityError ); this.addEventListener( AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler ); this.addEventListener( IOErrorEvent.IO_ERROR, ioErrorHandler ); // TODO does not pass ...args into the super function super.connect( url ); } /** * setID */ public function setId( id:Number ):* { //Logger.debug( "FMSConnection::setId: id=" + id ); if( isNaN( id ) ) return; clientID = id; return "Okay"; } /** * Status Handler for the NetConnection class */ private function netStatusHandler( event:NetStatusEvent ):void { switch( event.info.code ) { case "NetConnection.Connect.Success": //Logger.debug( "FMSConnection:netStatusHandler:Success: connected: " + this.connected ); dispatchEvent( new Event( "success" ) ); break; case "NetConnection.Connect.Failed": //Logger.debug( "FMSConnection:netStatusHandler:Failed: connected: " + this.connected + " - " + event.info.code ); dispatchEvent( new Event( "failed" ) ); break; default: //Logger.debug( "FMSConnection:netStatusHandler:code: " + event.info.code ); break; } } private function netSecurityError( event:SecurityErrorEvent ):void { //Logger.error( "FMSConnection:netSecurityError: " + event ); } private function asyncErrorHandler( event:AsyncErrorEvent ):void { //Logger.error( "FMSConnection:asyncErrorHandler: " + event.type + " - " + event.error ); } private function ioErrorHandler( event:IOErrorEvent ):void { //Logger.error( "FMSConnection:asyncErrorHandler: " + event.type + " - " + event.text ); } } }
FLVがサーバーに保存されるのであとはffmpegを利用すれば、サムネイルの作成や、音声の抽出、aviへの変換、携帯動画への変換など、いっぱい遊べると思います。
最新のwindowsなffmepgはこちらからダウンロードできます。
http://arrozcru.no-ip.org/ffmpeg_builds/
メモ的ffmpegの簡易コマンド
サムネイルの作成
ffmpeg -i video.flv -f image2 -ss 00:00:10 -vframes 1 thum.jpg
FLVをすべて画像に変換
ffmpeg -i video.flv -f image2 -vcodec mjpeg %05d.jpg
音声の抽出
ffmpeg.exe -y -i video.flv -ar 16000 video.wav
AVIへの変換
ffmpeg -i video.flv video.avi
携帯動画への変換
ffmpeg -i video.flv video.3gp
↑これでいいのかな?こちらは試してません(汗