▼Flash Media Server 2メモ▼
Flash Media Server 2


Flash Media Server 2
オーディオとビデオをFlashクライアントにストリーミング配信可能なサーバー。
主な用途は次の通り。

情報源


リンク
情報
Flash Media Server 2 APIリファレンス
Flash Media Server 2 セットアップガイド
Flash Media Server デベロッパーセンター
Flash Media Server(FMS)に関する有用サイトまとめ


開発ツール


Flash Media Server 2
開発用のDeveloper Editionは無償。価格は参考価格ページを参照。
ダウンロードしてインストール。ポート(デフォルトは1935)とアドミン名とパスワードを設定。

Flex 2
もちろん「Flash Professional 8」「Flash C3」でもFMSに接続することは可能だが、
無償版であるので「Flex 2 SDK」を利用した。

サーバーの開始と停止


サーバーの開始と停止
Flash Media Server 2には次の2種類のサーバが存在する。
Flash Media Admin Server 2 FMS2を管理するためのサーバ
Flash Media Server 2 実処理を行うサーバ

サーバーの開始と停止はスタートメニューで操作する。
「Macromedia→Flash Media Server→Start Flash Media Admin Server 2」
「Macromedia→Flash Media Server→Start Flash Media Server 2」
「Macromedia→Flash Media Server→Stop Flash Media Admin Server 2」
「Macromedia→Flash Media Server→Stop Flash Media Server 2」

初期状態ではPCを起動するたびにサーバーを開始してしまう。開発時にしか利用しない時は、「コントロールメニュー→管理ツール→サービス」で「Flash Media Admin Server 2」と「Flash Media Server 2」を「自動」から「手動」に変更すとよい。


Management Console


Management Console
Management Consoleはサーバの状態を閲覧するためのコンソール。
主な機能は次の通り。 利用するにはサーバ起動後、スタートメニュー「Macromedia→Flash Media Server→Management Console」を選択。


はじめてのFMSアプリケーション


サーバ側の準備
  1. FMS2を開始。
  2. FMS2のアプリケーションフォルダ(C:\Program Files\Macromedia\Flash Media Server 2\applications)に「アプリ名」("test")のフォルダを生成する。
  3. パーミッション付きのフォルダの時は707を設定し実行可能にする。


クライアント側の準備
  1. Flex 3でSWFを作成。
    package {
        import flash.display.*;
        import flash.events.*;
        import flash.net.*;
        import flash.text.*;
       
        //FMSへの接続
        public class FMSConnectEx extends Sprite {
            private var textField:TextField;
            private var nc:NetConnection;
        
            //コンストラクタ
            public function FMSConnectEx() {
                //テキストフィールドの生成
                textField=new TextField();
                textField.text="FMSConnectEx";
                addChild(textField);
    
                //ネットコネクション
                nc=new NetConnection();
                nc.objectEncoding=ObjectEncoding.AMF0;//←コレ重要
                nc.addEventListener(NetStatusEvent.NET_STATUS,onNetStatus);
                nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR,onSecurityError);
                
                //rtmp://サーバー名/アプリ名/{任意}インスタンス名
                //rtmp:/アプリ名/{任意}インスタンス名
                nc.connect("rtmp:/test");
            }
            
            //ネットステータスのイベント処理
            private function onNetStatus(evt:NetStatusEvent):void {
                switch(evt.info.code) {
                    case "NetConnection.Connect.Success":
                        textField.text="接続成功";
                        break;
                    case "NetConnection.Connect.Closed":
                        textField.text="接続解除";
                        break;
                    case "NetConnection.Connect.Failed":
                        textField.text="接続失敗";
                        break;
                    case "NetConnection.Connect.Rejected":
                        textField.text="接続拒否";
                        break;
                    default:
                        textField.text=""+evt.info.code;
                }
            }
                    
            //セキュリティーエラーのイベント処理
            private function onSecurityError(evt:SecurityErrorEvent):void {
                textField.text="セキュリティエラー";
            }
        }
    }
  2. 実行して「接続成功」と表示されたら成功。
  3. Manage ConsoleのView Applicationsを見るとtestが追加され、クライアントが1つ接続していることを確認できる。


複数クライアント間の共有オブジェクト


リモート共有オブジェクトを使って複数クライアント間の共有オブジェクトを作成する。


サーバ側の準備
  1. FMS2を開始。
  2. FMS2のアプリケーションフォルダ(C:\Program Files\Macromedia\Flash Media Server 2\applications)に「アプリ名」("test")のフォルダを生成する。
  3. パーミッション付きのフォルダの時は707を設定し実行可能にする。


クライアント側の準備
  1. Flex 3でSWFを作成。
    package {
        import flash.display.*;
        import flash.events.*;
        import flash.net.*;
        import flash.text.*;
        import flash.ui.*;
    
        //チャット
        public class ChatEx extends Sprite {
            private var tfView:TextField;
            private var tfSend:TextField;
            private var nc:NetConnection;
            private var so:SharedObject;
            
            //コンストラクタ
            public function ChatEx() {
                //テキストフィールドの生成
                tfView=addTextField(10,10,220,160);
                tfSend=addTextField(10,180,220,20);
                tfSend.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown);
    
                //ネットコネクション
                nc=new NetConnection();
                nc.objectEncoding=ObjectEncoding.AMF0;
                nc.addEventListener(NetStatusEvent.NET_STATUS,onNetStatus);
                nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR,onSecurityError);
                nc.connect("rtmp:/test");
            }
    
            //テキストフィールドの追加
            private function addTextField(x:int,y:int,w:uint,h:uint):TextField {
                var textField:TextField=new TextField();
                addChild(textField);
                textField.x=x;
                textField.y=y;
                textField.width=w;
                textField.height=h;
                textField.text="";
                textField.selectable=true;
                textField.border=true;
                textField.type=TextFieldType.INPUT;
                return textField;
            }
    
            //ネットステータスイベントの処理
            private function onNetStatus(evt:NetStatusEvent):void {
                switch(evt.info.code) {
                    case "NetConnection.Connect.Success":
                        tfView.text="接続成功";
                        initSO();
                        break;
                    case "NetConnection.Connect.Closed":
                        tfView.text="接続解除";
                        break;
                    case "NetConnection.Connect.Failed":
                        tfView.text="接続失敗";
                        break;
                    case "NetConnection.Connect.Rejected":
                        tfView.text="接続拒否";
                        break;
                    default:
                        tfView.text=""+evt.info.code;
                }
            }
                    
            //セキュリティーエラーイベントの処理
            private function onSecurityError(evt:SecurityErrorEvent):void {
                tfView.text="セキュリティエラー";
            }
    
            //SharedObjectの初期化
            private function initSO():void {
                var text:String;
    
                //SharedObjectの取得
                so=SharedObject.getRemote("chat",nc.uri,false);            
                
                //SharedObjectの値変更イベントの処理
                so.addEventListener(SyncEvent.SYNC,function(evt:SyncEvent):void  {
                    text=(so.data.text==undefined)?"":so.data.text;
                    if (text.length==0) return; 
                    tfView.text=text;
                });
    
                //SOにアクセス
                so.connect(nc);
            }
    
            //キーダウンイベントの処理
            private function onKeyDown(evt:KeyboardEvent):void {
                var text:String;
                if (so==null) return;
                if (evt.keyCode==Keyboard.ENTER) {
                    text=(so.data.text==undefined)?"":so.data.text;
                    if (tfSend.text.length==0) return;
                    text=tfSend.text+"\n"+text;
                    so.setProperty("text",text);
                    tfSend.text="";
                }
            }
        }
    }
  2. 実行して「接続成功」と表示されたら成功。複数のswfを開き、下のテキストフィールドに入力することでチャットできることを確認。
      


ストリーミング配信


FLVファイルのストリーミング配信を行うFlashコンテンツを作成する。


サーバ側の準備
  1. FMS2を開始。
  2. FMS2のアプリケーションフォルダ(C:\Program Files\Macromedia\Flash Media Server 2\applications)に「アプリ名/streams/インスタンス名/」("test/stream/streaming")のフォルダを生成し、FLVを配置。
  3. パーミッション付きのフォルダの時は707を設定し実行可能にする。


クライアント側の準備
  1. Flex 3でSWFを作成。
    package {
        import flash.display.*;
        import flash.events.*;
        import flash.media.*;
        import flash.net.*;
    
        //動画ストリーミングの再生
        public class FMSVideoEx extends Sprite {
            private var nc:NetConnection;
            private var stream:NetStream;
            private var playFlag:Boolean;
    
            //コンストラクタ
            public function FMSVideoEx() {
                //ネットコネクションの生成
                nc=new NetConnection();
                            nc.objectEncoding=ObjectEncoding.AMF0;//←コレ重要
                nc.addEventListener(NetStatusEvent.NET_STATUS,onNetStatus);
                nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR,onSecurityError);
                nc.connect("rtmp://localhost/test/streaming");//←接続先
            }
    
            //ネットステータスのイベント処理
            private function onNetStatus(evt:NetStatusEvent):void {
                switch (evt.info.code) {
                    //成功
                    case "NetConnection.Connect.Success":
                        //ストリームの生成
                        var stream:NetStream=new NetStream(nc);
                        stream.addEventListener(NetStatusEvent.NET_STATUS,onNetStatus);
                        stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR,onAsyncError);
    
                        //ビデオの生成
                        var video:Video=new Video(128,96);
                        video.x=56;
                        video.y=72;
                        addChild(video);
                        video.attachNetStream(stream);
                        
                        //ビデオの再生
                        stream.play("sample");//←拡張子は付けない
                        break;
                    //ファイルが見つからない
                    case "NetStream.Play.StreamNotFound":
                        trace("ファイルが見つからない");
                        break;
                }
            }
    
            //セキュリティーエラーのイベント処理
            private function onSecurityError(evt:SecurityErrorEvent):void {
                trace("セキュリティーエラー");
            }
    
            //同期エラーのイベント処理        
            private function onAsyncError(evt:AsyncErrorEvent):void {
                trace("同期エラー");
            }
        }
    }
  2. 実行して「接続成功」と表示されたら成功。


サーバーサイドスクリプト


サーバーサイドアプリケーションの開始と停止、クライアントの接続と切断をLiveLogに出力するサーバーサイドアプリケーションを作成する。


サーバ側の準備
  1. FMS2を開始。
  2. FMS2のアプリケーションフォルダ(C:\Program Files\Macromedia\Flash Media Server 2\applications)に「アプリ名」("test")のフォルダを生成する。
  3. パーミッション付きのフォルダの時は707を設定し実行可能にする。
  4. アプリ名のフォルダ("test")に"main.asc"というファイルを生成し、以下のソースコードを記述。文字コードはUTF-8で保存。
    //アプリ開始
    application.onAppStart=function() {
        trace("アプリ開始");
    }
    
    //クライアント接続
    application.onConnect=function(client) {
        this.acceptConnection(client);
        trace("クライアント接続");
    };
    
    //クライアント切断
    application.onDisconnect=function(client) {
        trace("クライアント切断");
    }
    
    //アプリ終了
    application.onAppStop=function() {
        trace("アプリ終了");
    };
  5. サーバーサイドスクリプトを変更した時は、Management Consoleのアプリケーションのリロードボタンを押して反映。
  6. LiveLogにサーバーサイドスクリプトでのトレースが表示されていることを確認。


クライアント側の準備
  1. 上のサンプル("test"に接続するもの)を起動してLiveLogのトレースを確認。


サーバーサイドSharedObject


サーバーサイドSharedObjectを使って、サーバと接続しているクライアント数を保持する。
SharedObjectへの同時アクセスによる矛盾が生じないように、lock()とunlock()を使っている。


サーバ側の準備
  1. FMS2を開始。
  2. FMS2のアプリケーションフォルダ(C:\Program Files\Macromedia\Flash Media Server 2\applications)に「アプリ名」("test")のフォルダを生成する。
  3. パーミッション付きのフォルダの時は707を設定し実行可能にする。
  4. アプリ名のフォルダ("test")に"main.asc"というファイルを生成し、以下のソースコードを記述。文字コードはUTF-8で保存。
    //アプリ開始
    application.onAppStart=function() {
        trace("アプリ開始");
    }
    
    //クライアント接続
    application.onConnect=function(client) {
        trace("クライアント接続>"+client.ip);
    
        //クライアント接続を許可
        this.acceptConnection(client);
        
        //ユーザー数の加算
        so=SharedObject.get("test");
        so.lock();
        count=so.getProperty("count");
        if (count==null) count=0;
        count++;
        so.setProperty("count",count);
        so.unlock();    
        trace("ユーザー数>"+count);
    };
    
    //クライアント切断
    application.onDisconnect=function(client) {
        trace("クライアント切断");
    
        //ユーザー数の減算
        so=SharedObject.get("test");
        so.lock();
        count=so.getProperty("count");
        if (count==null) count=1;
        count--;
        so.setProperty("count",count);
        so.unlock();
        trace("ユーザー数>"+count);
    }
    
    //アプリ終了
    application.onAppStop=function() {
        trace("アプリ終了");
    };


クライアント側の準備
  1. 上のサンプル("test"に接続するもの)を起動してLiveLogのトレースを確認。

メディアクラス


クライアントサイドクラス
Cameraクラス ビデオキャプチャ
Microphoneクラス 音声キャプチャ
MovieClipクラス オーディオリソースをローカル再生するか、FMS経由でストリーム再生するかを指定
NetConnectionクラス FMSとの接続
NetStreamクラス FMSとのストリーム
SharedObject 複数のクライアント間でのデータ共有
Systemクラス プロパティの保持
Videoクラス ビデオストリームを再生

サーバーサイドクラス
Applicationクラス アプリケーションがアンロードされるまでに存続する情報を保持
Clientクラス FMSへ接続しているクライアントの接続を処理
LoadVarsクラス リモートもくしはローカルから変数を読み込む
Logクラス ログ
NetConnectionクラス アプリケーションサーバ、アプリケーションインスタンスとの接続
SharedObjectクラス 複数のクライアント間でデータ共有
SOAPCall 全てのWebサービス呼び出しから返されるオブジェクトタイプ
Streamクラス ストリーム
WebServiceクラス Webサービス
XMLクラス XML
XMLSocketクラス XMLソケット
XMLStreamsクラス XMLストリーム


クライアントとサーバの通信


NetConnectionによってクライアントがサーバと接続すると、サーバーサイドのClientオブジェクトが生成される。

クライアントサイドとサーバーサイドで関連付けられているもの
クライアントサイド サーバーサイド
my_nc(NetConnectionオブジェクト) my_client(Clientオブジェクト or application.clientオブジェクト)
my_ns(NetStreamオブジェクト) my_server_stream(Streamオブジェクト)
my_so(リモート共有オブジェクト) my_server_so(サーバーサイド共有オブジェクト)

クライアントサイドの呼び出しによってサーバーサイドの呼び出しが開始
クライアントサイドの呼び出し 開始するサーバーサイドの呼び出し
my_nc.connect application.onConnect
my_nc.close application.onDisconnect
my_nc.call("doThing",1,"foo"); my_client.doThing(1,"foo");
my_so.send("doThing",1,"foo") my_server_so.doThing(1,"foo");

サーバーサイドの呼び出しによってクライアントサイドの呼び出しが開始
サーバーサイド 開始するクライアントサイド
my_client.call("doThing",myCallbackFcn,1,"foo"); my_nc.doThing(1,"foo");
my_server_stream.send("doThing",1,"foo"); my_ns.doThing(1,"foo");
my_server_so.send("doThing",1,"foo"); my_so.doThing(1,"foo");




−戻る−