Network

Archives

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)

Photo

PROFILE

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

Designer / Programmer

RECENT POSTS

MONTHLY ARCHIVES