ActionScript3でJPEG

2009.10.18 日曜日

Flashで生成したBitmapDataをJPEG操作する際のメモ。

エンコード(BitmapData→JPEG)は、「as3corelib」のJPGEncoderクラスでもいいですが、FLASH Player 10 以上であれば、Vector型配列に最適化されている「Optimized JPEG Encoder」の方が処理速度が速いのでお勧め。デコード(JPEG→BitmapData)は、ちょっと面倒ですがLoaderクラスを使います。

PNGのエンコードには「as3corelib」を使うのが簡単だと思います。デコードはJPEGと同じ方法で問題ありません。

下記サンプルコードは、起動時にノイズ画像を生成して描画、それをJPEGにエンコード後、デコードして描画しています。Encode()の第2引数が圧縮率で100が最も画像がきれい(低圧縮)になります。Decode()は第1引数のByteArrayを第2引数のBitmapDataに描画します。

package {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.utils.ByteArray;

	public class Main extends MovieClip {

		//初期設定
		public function Main():void {

			//画像(ノイズ)を生成
			var src:BitmapData = new BitmapData(300, 300);
			src.noise(0);

			//画像をJPEGにエンコード後、デコード
			var result:BitmapData = new BitmapData(300, 300)
			Decode(Encode(src, 0), result);

			//元画像を表示
			var SampleA:Bitmap = new Bitmap(src);
			this.addChild(SampleA);

			//JPEG圧縮後の画像を表示
			var SampleB:Bitmap = new Bitmap(result);
			this.addChild(SampleB);
			SampleB.x = SampleA.width;
		}

		//エンコード
		private function Encode(_bmp:BitmapData, _quality:uint = 50):ByteArray {

			//エンコーダーの設定
			var encoder:JPEGEncoder = new JPEGEncoder(_quality);
			var data:ByteArray = new ByteArray();
			data = encoder.encode(_bmp);

			//バイト配列を返す
			return (data);
		}

		//デコード
		private function Decode(_data:ByteArray, _bmp:BitmapData):void {

			//バイト配列の読み込み
			var loader:Loader = new Loader();
			loader.loadBytes(_data);
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, Loaded);

			//読み込みが完了した場合
			function Loaded(e:Event):void {

				//描画
				_bmp.draw(loader);
			}
		}
	}
}

http://www.inazumatv.com/contents/archives/3573

Posted by tmdf||comments (0)

Flash間データ通信:Flash Media Server 2

2009.10.16 金曜日

前回の記事で紹介した「Flash Media Server」を試す機会があったので、簡単なライブ映像のストリーミングサンプルをアップします。

下記サンプルコードを試すには、「Flash Media Server」をインストールする必要があります。インストールは前回の記事でも紹介した「Flash Media Server 2 メモ」などを参考にしてください。あと、「C:Program FilesAdobeFlash Media Server 3.5applications」に「test」フォルダが必要。

下記プログラムを実行すると、起動時にWebカメラの映像をそのまま表示、キーボードの「1」を押すと配信開始、「2」を押すと受信して表示します。

注意点は、受信時にvideoをそのままステージに追加していますが、これをBitmapDataにdraw()する場合、セキュリティエラーでひっかかるのでクロスドメイン関係の処理が必要になります。また以下はローカルホストで作業していますが、ネットワーク越しに作業する場合はポート番号1935を開放して下さい。

package {
	import flash.display.MovieClip;
	import flash.events.KeyboardEvent;
	import flash.events.NetStatusEvent;
	import flash.media.Camera;
	import flash.media.Video;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	import flash.net.ObjectEncoding;

	public class Main extends MovieClip {

		//Webカメラ
		private var camera:Camera;

		//通信
		private var nc:NetConnection;

		//初期設定
		public function Main():void {

			//Webカメラの設定、配置
			camera = Camera.getCamera("0");
			camera.setMode(320, 240, 30, false);
			var video:Video = new Video(camera.width, camera.height);
			video.attachCamera(camera);
			this.addChild(video);

			//FMSの設定
			nc = new NetConnection();
			nc.objectEncoding = ObjectEncoding.AMF0;
			nc.addEventListener(NetStatusEvent.NET_STATUS, CheckFMS);
			nc.connect("rtmp://127.0.0.1/test");

			//キーボード
			stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDown);
		}

		//FMSステータス確認
		private function CheckFMS(e:NetStatusEvent):void {

			//取得
			var info:String = e.info.code;
			var status:String;

			//ステータス出力
			if (info == "NetConnection.Connect.Success") trace("Connected");
			else if (info == "NetConnection.Connect.Closed") trace("Closed");
			else if (info == "NetConnection.Connect.Failed") trace("Failed");
			else if (info == "NetConnection.Connect.Rejected") trace("Rejected");
		}

		//キーボードが押された場合
		private function KeyDown(e:KeyboardEvent):void {

			//ストリーミング変数
			var stream:NetStream;

			//「1」
			if (e.keyCode == 49) {

				//Webカメラの映像を配信
				stream = new NetStream(nc);
				stream.attachCamera(camera);
				stream.publish("sample");

				//ステータス出力
				trace("Send")
			}

			//「2」
			else if (e.keyCode == 50) {

				//ストリーミングを受信
				stream = new NetStream(nc);
				var video:Video = new Video(camera.width, camera.height);
				video.attachNetStream(stream);
				this.addChild(video);
				video.x = camera.width;
				stream.play("sample");

				//ステータス出力
				trace("Receive")
			}
		}
	}
}

Posted by tmdf| |comments (0)

Flash間データ通信:Flash Media Server

2009.10.4 日曜日

複数のFlash間のデータ通信に特化したAdobe純正サーバーソフトが「Adobe – Flash Media Server」です。本気仕様のサーバーソフトなので、同時アクセスが10台以下なら無償のデベロッパー版を試せますが、残念ながらWindowsとLinuxでしか動作しません。以前のバージョンですが「Flash Media Server 2 メモ」で、詳しく使い方などが掲載されているので参考にして下さい。

上記Webサイトを参考に、自分のWebカメラの映像を、ストリーミング用のクラスではなく、サーバーに作ったShareObjectを使ったバイト配列のやりとりで行おうとしたら320x240x5fpsぐらいしかスピードが出ませんでした。リアルタイムな映像の共有はストリーム用のクラスでやるべきみたいです。試す機会があったら追記します。

Posted by tmdf| |comments (0)

Flash間データ通信:FlashLCS

2009.10.3 土曜日

前々回に紹介したLocalConnectionを独自に拡張した「FlashLCS」なるものが、今年登場していたようです。公式ブログ?によると20Mbpsくらい速度が出るそうなので、このライブラリを使っても40KB制限を気にせずに済みそうです。

この記事を書いている現在、google codeで公開されているページにはサンプル、リファレンスがないのでよく分かりませんでしたが、FASH CS4にasタイプのライブラリを使ってコンパイルするとエラーが出ていました。swcの方は試していません。

日本語の情報もあまりありませんが、「alt 無制限に双方向通信が可能なLocalConnectionクラス「FlashLCS」」でテストされている方がいます。サンプルコードも配布されているようですが、このページで使われているライブラリは、現在配られているものより少し古いバージョンのようで、こちらのライブラリを流用するとうまくコンパイルできました。

実験で書いた下記サンプルコードで、Webカメラの映像をリアルタイムに送信してみたところ、自分の環境では少し(0.2秒くらい)遅れてました。LCS_B → LCS_A の順に起動してデータを送信します。

//LCS_A.as
package {
	import com.blitzagency.rpc.LocalConnectionService;
	import flash.display.BitmapData;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.media.Camera;
	import flash.media.Video;
	import flash.utils.ByteArray;

	public class LCS_A extends MovieClip {

		//データ送受信用変数
		private var lcs:LocalConnectionService;

		//Webカメラ用変数
		private var video:Video;

		//初期設定
		public function LCS_A():void {

			//LCS設定
			lcs = new LocalConnectionService();
			lcs.client = this;
			lcs.connect();

			//Webカメラ設定
			var camera:Camera = Camera.getCamera("2");
			camera.setMode(320, 240, 30);

			//ビデオ設定
			video = new Video(camera.width, camera.height);
			video.attachCamera(camera);

			//リスナー登録
			this.addEventListener(Event.ENTER_FRAME, Update);
		}

		//データ送信
		private function Update(e:Event):void {

			//Webカメラの映像をバイト配列に変換
			var bmd:BitmapData = new BitmapData(320, 240, false);
			bmd.draw(video);
			var data:ByteArray = new ByteArray();
			data = bmd.getPixels(bmd.rect);
			data.position = 0;

			//描画更新
			lcs.Update(data);
		}
	}
}
//LCS_B.as
package {
	import com.blitzagency.rpc.LocalConnectionService;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.MovieClip;
	import flash.utils.ByteArray;

	public class LCS_B extends MovieClip {

		//データ送受信用変数
		private var lcs:LocalConnectionService;

		//画面表示用変数
		private var bmd:BitmapData;
		private var bmp:Bitmap;

		//初期設定
		public function LCS_B():void {

			//LCS設定
			lcs = new LocalConnectionService();
			lcs.client = this;
			lcs.connect();

			//ビットマップ設定
			bmd = new BitmapData(320, 240, false);
			bmp = new Bitmap(bmd);
			this.addChild(bmp);
		}

		//描画更新
		public function Update(_data:ByteArray):void {

			//バイト配列を画像に変換
			_data.position = 0;
			bmd.setPixels(bmd.rect, _data);
		}
	}
}

Posted by tmdf| |comments (0)

Flash間データ通信:Socket

2009.10.2 金曜日

異なるPCで起動しているFlash間で通信を行いたい場合、TCPのソケットサーバーを間に立て、Socket通信を行うのがベストだと思います。FLASHはTCPといった遅い通信プロトコルを採用しているため、処理の重いString型を扱うXML Socket通信より、ByteArray型を扱うSocket通信を使った方が、若干スピードのメリットがあります。

話は逸れますが、Flashの扱えるTCPは信頼性>速度の通信プロトコルのため、データを確実に送ることはできますが、大容量のデータを送り続けた場合には数十秒のタイムラグが発生します。現在のFlashでは扱えないUDPは、信頼性<速度の通信プロトコルなので、いつかサポートしてほしいところです。プロトコルの詳しい情報はWikipediaの「TCP」と「UDP」などをご覧ください。

修正 2010.8.13
AIR2.0からUDPのサポート、TCPサーバー(以前はクライアントだけ)にもなれます。

Flashの間に立てるソケットサーバーですが、フリーウェアでは「Socket Debugger (Free)」があります。このソフトウェアはバッファサイズが99,999Byteが限度のようなので、大きいデータのリアルタイムなやりとりには向きませんが、ちょっとしたテスト環境の構築には便利です。

また、単純なTCPサーバーならProcessingでも作れます。下記コードでは「Flash clientA」からポート3000を通して「Processing serverA」にデータを送り、受信したデータを「Processing serverB」からポート3001経由で「Flash clientB」に戻しています。動作検証用のサンプルなので、同じPC内(localhost)にサーバーとクライアントの全てを起動させていますし、接続確認や細かいエラー処理などは省いています。Processing → FLASHの順に起動し、FLASHの画面をクリックするとデータの送受信を行います。

//Server.pde
import processing.net.*;

//サーバー変数
Server serverA;
Server serverB;

//初期設定
void setup() {

	//サーバーの作成
	serverA = new Server(this, 3000);
	serverB = new Server(this, 3001);
}

//常時実行
void draw() {

	//serverAに接続しているクライアントを取得
	Client client = serverA.available();

	//クライアントがいた場合
	if (client != null) {

		//データを取得
		String data = client.readString();

		//データがある場合
		if (data != null) {

			//serverBに書き込み
			serverB.write(data);
		}
	}
}
//Client.as
package {
	import flash.display.MovieClip;
	import flash.events.MouseEvent;
	import flash.events.ProgressEvent;
	import flash.net.Socket;

	public class Client extends MovieClip {

		//クライアント変数
		private var clientA:Socket;
		private var clientB:Socket;

		//初期設定
		public function Client():void {

			//リスナー登録
			stage.addEventListener(MouseEvent.CLICK, Send);

			//clientA serverAに接続
			clientA = new Socket();
			clientA.connect(&amp;quot;localhost&amp;quot;, 3000);

			//clientB serverBに接続、リスナー登録
			clientB = new Socket();
			clientB.addEventListener(ProgressEvent.SOCKET_DATA, Recieve);
			clientB.connect(&amp;quot;localhost&amp;quot;, 3001);
		}

		//データ送信
		private function Send(e:MouseEvent):void {

			//データを書き込み
			clientA.writeUTFBytes(String(Math.random()));

			//serverAに送信
			clientA.flush();
		}

		//データ受信
		private function Recieve(e:ProgressEvent):void {

			//受信した文字データを出力
			trace(clientB.readUTFBytes(clientB.bytesAvailable));
		}
	}
}

Posted by tmdf| |comments (0)

Flash間データ通信:LocalConnection

2009.10.1 木曜日

普通は使うシチュエーションがあまりないと思いますが、複数のFlash間でのデータ通信の方法を数回にわたって紹介します。

まず最初は、同じPCで起動している複数のFlash間の通信を行いたい場合。これはLocalConnection(以下LC)が簡単。LCのメリットは、別のFlashの関数を呼び出して引数を直接渡せるところ。デメリットは40KBまでしか一度にデータを送れない、片方向通信なので双方向にするために2つのLCを作らないといけない、FLASHが強制終了した場合などにLCの切断(再接続)がうまくできない時がある、など。

このデメリットの中でも40KB制限が個人的には一番のネック。これの解決方法として思いついたのが、データ本体のやりとりには40KB制限のないShareObjectを使い、データ更新のプッシュ通知のみにLCを使った通信環境です。通信速度を正確に測ったことはありませんが、Webカメラの映像(320x240x30fps)をByteArray型に変換してリアルタイムに送信できてそうでした。ShareObjectを使っているので、ArrayやXMLなどのデータもそのまま保持できるのもメリットです。こういったShareObjectの使い方は、今後紹介しようと思っている「Flash Media Server」と似ています。

下記のサンプルコードは「LC_A」の画面クリックでデータ送信(SharedObject書き込み)、LCでプッシュ通知を受けた「LC_B」が受信(SharedObject読み込み)となっています。ポイントはSharedObject.getLocal()の第2引数のパスを設定するところ。サンプルのように”/”でなくてもいいですが、ここを共通のパスにしておかないとうまく動きません。また、このサンプルを試すにはFlash Playerの設定で「ローカル記憶容量」とプライバシーの高度な設定で「グローバルセキュリティ設定」を調整する必要があります。

//LC_A.as
package {
	import flash.display.MovieClip;
	import flash.events.MouseEvent;
	import flash.net.LocalConnection;
	import flash.net.SharedObject;

	public class LC_A extends MovieClip {

		//初期設定
		public function LC_A():void {

			//リスナー登録
			stage.addEventListener(MouseEvent.CLICK, Send);
		}

		//データ送信
		private function Send(e:MouseEvent):void {

			//ShareObject設定
			var so:SharedObject = SharedObject.getLocal("tmdf", "/";);

			//データを設定
			so.data.mouseX = mouseX
			so.data.mouseY = mouseY;

			//データを書き込み
			so.flush();

			//LocalConnectionでプッシュ通知
			var lc:LocalConnection = new LocalConnection();
			lc.send("tmdf", "Push");
		}
	}
}
//LC_B.as
package {
	import flash.display.MovieClip;
	import flash.net.LocalConnection;
	import flash.net.SharedObject;

	public class LC_B extends MovieClip {

		//データ送受信用変数
		private var lc:LocalConnection;

		//初期設定
		public function LC_B():void {

			//LocalConnection設定
			lc = new LocalConnection();
			lc.client = this;
			lc.connect("tmdf");
		}

		//プッシュ通知
		public function Push():void {

			//ShareObject設定
			var so:SharedObject = SharedObject.getLocal("tmdf", "/");

			//出力
			trace(so.data.mouseX, so.data.mouseY);
		}
	}
}

Posted by tmdf| |comments (1)

サドル購入

2009.9.24 木曜日

Saddle

「BONTRAGER Inform R サドル」の型落ち(たぶん一つ前のモデル)商品でお値段は3,600円(定価6,000円から40%オフ)。サイズはちゃんとお店にある坐骨幅の計測器で調べて、Mの146mm。Gaap Street に標準で付いている「VELO VL-1050 / Black&Silver」のサドルよりはちょっと硬いかも。幅とか長さは一緒ぐらい。まだ使い始めたばかりなので、いまいちポジションがしっくりしません。そのうちレビューを追記したいと思います。

1年以上使っていますが、ちゃんとポジションを出してからはお尻も痛くありません。値段の割にしっかりしています。見た目の安っぽさを我慢できればかなりお買い得。

Posted by tmdf| |comments (0)

サイクルコンピューター購入

2009.9.16 水曜日

GARMIN Edge 705

以前の投稿で次に買うサイクルコンピューターの候補をいくつか挙げておきながら、候補外だった「GARMIN Edge 705」を買っちゃいました。

細かい部分は端折って紹介しますが、スピード、ケイデンス、心拍数はもちろん、GPSと連動した地図(ルート案内)や高度、オートラップなど思いつく機能は全て網羅。GPSの捕捉(起動)も30秒くらいと非常に早く、内臓バッテリーも10時間以上はもちます。何より走行データを簡単にMacで管理できるってところが決め手。接続・充電もUSBケーブル1本で楽。
本体は昔のモトローラの携帯電話に似た雰囲気のアメリカンデザインで、裏にわざわざ「Designed in USA」と彫っています。画面上下のRは手前のボタンの操作性を良くし、大きくC面カットされた縁と相まってコンパクトに見せつつバランスよくまとまっています。質感は本体と心拍センサーは普通ですが、スピード/ケイデンスセンサーは安っぽい感じ。
並行輸入品のため、表示言語が全て(地図も)英語ですが、特に難しい単語などはなく直感的に扱えると思います。ちょっとだけ画面の配色を選べれるのもいいです。

ネックは他の製品と比べて圧倒的に高い値段。今年初頭の円高(120円=1£)の頃は海外通販で、本体にスピード(ケイデンス)センサーと心拍センサーを付けたセットが4万円以下で買えたみたいですが、今のレートだとわざわざ個人輸入するほどお得でもないので、並行輸入品を国内の業者から約5万円で買いました。
また、ちゃんとした日本の地図は入っていないので別途入手(しっかりした地図は約1万円、地域が限定されますがフリーの地図もあります)する必要があります。どちらにせよ地図表示は最近の携帯電話やPNDのようなものではなく、10年以上前の雰囲気です。
あとは、チェーンステーに取り付ける一体型タイプのセンサーは、Gaap Streetのようにスピード(スポーク)とケイデンス(クランク)が離れている場合は片方しか届きません。「GARMIN Edge 705」の場合、スピード計測にはGPSをメインで使うので、私はケイデンス用と割り切っています。妻のTarutaruga Type Sport だとギリギリで届きます。

液晶保護フィルムは家に余っていたiPhone用のモノを切って自作しました。需要があるかどうか分かりませんが切るときに使った型紙「garmin.pdf」をダウンロードできるようにしておきました。適当に作ったので精度はありません。編集可能なPDFなので自分で修正してください。

Posted by tmdf| |comments (0)

パンク修理

2009.9.15 火曜日

Pump

先日、2回後輪がパンクしました。走行距離2,200kmだからもう少し寿命は先っぽいですが、2箇所もパンク修理したチューブをずっと使うのも嫌なので今度新品と交換しようかなっと思っています。

パンク修理やチューブの交換は、不器用な私でもできるのでかなり簡単な作業だと思います。参考にしたサイトは「意外と簡単!自転車のパンク修理 All About」など。色んなサイトで細かく解説してくれています。
ポイントはリムやタイヤの内側を念入りにキレイにすること。砂利とかがちょっと入っているとまたすぐパンクします。あと、パンク修理の際にチューブに貼るパッチは渾身の力をかける。意外と2つ穴が開いてるかもしれないの入念に空気が漏れていないかチェックする。まとめると面倒臭がらず丁寧にやるってことです。

チューブはGaap Streetに標準で使われている20×1.5/1.75(米式)が近くのお店になかったので、「SCHWALBE 20×1.50/2.50」にしました。お値段は840円/本。ちなみにパンク修理には「マルニ 修理キットNo.1」を使っています。600円くらい。

ついでにいつも携帯しているツールも紹介。
携帯ポンプは見た目のみで「BONTRAGER エアサポートプラスミニポンプ」。あとは交換用チューブと「Tacx タイヤレバー」、携帯工具の「TOPEAK ミニ9」、ライトやサイコン用に「CR2032電池」をいくつか。これらを「Tacx Tool-Tube」に入れてボトルケージに挿してます。

Posted by tmdf| |comments (0)

ヒップバッグ購入

2009.9.13 日曜日

Hip-Bag

昔から使っていたヒップバッグが古くなってきたので、「HAGLOFS RAMBLER EVO LUMBER PACK」を買いました。ネットオークションで新品を4,800円でゲット。

このバッグは大きく分けて、メインの収納、サイドのボトルポケットx2、ウェストベルトのポケットx2、背面の拡張可能なヘルメットホルダー?から成り立ってます。メーカー表記の9Lはどこまでを含んでの容量なのかは不明。何にせよヒップバッグにしてはかなり大きいと思います。

まず、メインの収納はデジタル一眼レフ(Nikon D40)を入れてもタオルなどが入る余裕はありますし、ジッパーで開く蓋の裏と、背中に当たる面にメッシュのポケットがあってちょっと便利。サイドのボトルポケットは、深さもしっかりあり、ベルトで幅を調整可能で500mlペットボトルはもちろん、自転車用ボトルもちゃんと入って走行中でも出し入れしやすくて便利です。
ウェストのポケットは、見た目より大きくてiPhoneが4つ分くらいのスペース。裏面(腰に当たる部分)はクッション性があって良い締め心地。ただし、そんなに太い人いるの?ってくらいベルトが長くて余ります。
最後に拡張できるヘルメットホルダーですが、最大にすると自転車用ヘルメットが余裕で入るスペースが確保でき、幅はB5(約20cm)が入るくらい。上面は開いていて側面はベルトしかないので用途も限られますが、出先で袋に入ったモノを買った時やアウターを脱いだ時とかに使えます。

あと、自転車といえばメッセンジャーバッグ!みたいなノリで「TIMBUK2 Classic Messanger」のSサイズも以前買ってみました。性能(容量12.2L、防水)はいいのですが、自分の体格がバッグと合っていない(胸周りとかが細すぎ?)みたいで、頻繁にズレるし、ストラップを締めすぎると脇下が痛くなるので少し嫌になっています。あと背中が暑い。

Posted by tmdf||comments (0)

Photo

PROFILE

森田 考陽 [Takaaki Morita]
Twitter: @tmdf
Other: mtdf.net

Designer / Programmer

RECENT POSTS

MONTHLY ARCHIVES