Flash Media Server 應用筆記

來源:互聯網
上載者:User

最近做的一個基於FMS的Flash錄音示範,主要的幾個技術點:

 

1. FMS 安裝及使用

2. Flash 錄音及相應的安全性問題

3. 音效檔的轉換

 

1. FMS 安裝及使用

  • Flash Media Server 可從Adobe 官方網站下載:http://www.adobe.com/products/flashmediaserver/
    ,目前版本為3.5,FMS本身包含一系列的軟體,售價在幾千美元,作為開發人員可直接下載 Flash Media Development Server,FMS絕大部分的功能都支援。
  • 安裝,後台管理都比較簡單,可以百度一下 “FMS 入門教程”。
  • 流媒體預設使用 rtmp 協議通過 1935 連接埠發布,如果選擇安裝Apache,將支援 HTTP 轉寄,HTTP預設連接埠是80,因為我的伺服器本身已經部署了IIS,所以需要修改連接埠,具體的設定檔在 [FMS安裝目錄]/conf/fms.ini,及[FMS安裝目錄]/conf/_defaultRoot_/Adaptor.xml,具體可參考:http://help.adobe.com/en_US/FlashMediaServer/3.5_AdminGuide/WSE2A5A7B9-E118-496f-92F9-E295038DB7DB.html
    或 這裡

 

2. 用戶端串連及發布

 

        private var nc: NetConnection;
        private var ns: NetStream;

        nc = new NetConnection();
        nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
       
        private function onNetStatus(event:NetStatusEvent):void{
            ShowTrace("onNetStatus, code: " + event.info.code);
            switch(event.info.code){

                //串連成功
                case "NetConnection.Connect.Success":
                    break;
                case "NetStream.Play.Start":
                    break;
                case "NetConnection.Connect.Closed":
                case "NetConnection.Connect.Failed":
                case "NetConnection.Connect.Rejected":
                    if(flag)
                        ExternalInterface.call("flash_callback_proxy", "stopRecord");
                    else
                        ExternalInterface.call("flash_callback_proxy", "failRecord");
                    flag = false;
                    break;

                //開始錄製,在此之前還會收到 NetSteam.Publish.Start 事件
                case "NetStream.Record.Start":
                    ExternalInterface.call("flash_callback_proxy", "startRecord");
                    break;
            }
        }       

 

        //開始錄製

        private function startRecord(audioid : String) : Boolean
        {
            ShowTrace("Get js call startRecord");

            if(!nc.connected) {
                if(retryFlag == 0)
                {
                    ShowTrace("startRecord, need connect to server, count: " + retryFlag);
                    try
                    {
                        nc.connect("rtmp://192.168.5.2/demo"); //demo
                    }
                    catch(e) {
                        ShowTrace("startRecord, catch exception: " + e.toString());
                    }
                }

                retryFlag++;
                if(retryFlag > 3)
                    ShowTrace("startRecord, connect to server timeout!");
                else
                    intervalId = flash.utils.setTimeout(startRecord, 3000, audioid);
                return false;
            } else {
                flag = true;
                retryFlag = 0;
                flash.utils.clearTimeout(intervalId);
            }

            //Publish audio           
            if(!setupAudio())
                return false;
           
            publishAudio(audioid);

            micTimer.start();
            ShowTrace("startRecord, started, id: " + audioid);
            return true;
        }

        private function stopRecord()
        {
            ShowTrace("Get js call stopRecord");
           
            //Stop publish
            micTimer.stop();
           
            ns.close();
            ns = null;
           
            nc.close();
        }

 

3. 錄音及音量條的顯示

 

        以下幾個和錄音有關的函數,Flash 中當你啟動錄音擷取Micphone資料時,預設情況下是會彈出一個安全警告請求使用者允許的,有趣的是我的Flash怎麼也不出來,就算在程式中強制調用Security.showSettings(SecurityPanel.PRIVACY);也不行,滑鼠點右鍵設定菜單也是灰的,搞了好久才發現原來是我的Flash在網頁中的位置太小了,空間不夠彈出這個提示框。

 

        private function setupAudio():Boolean {
            //Security.showSettings(SecurityPanel.MICROPHONE);
            mic = Microphone.getMicrophone();
            if(mic == null)
            {
                ShowTrace("setupVideos, no micphone found!");
                return false;
            }
            else
            {
                ShowTrace("setupVideos, getMicrophone success!");
                mic.rate = 22; //使用 22K 採樣
                mic.setSilenceLevel(5, -1);
                mic.addEventListener(StatusEvent.STATUS, micStatusHandler);

                //mic.addEventListener(ActivityEvent.ACTIVITY, drawMicLevel);
            }
          
            return true;
        }

 

        private function publishAudio(audioid: String) {

            //Start publish
            ns = new NetStream(nc);
            ns.client = new CustomClient();
            ns.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
            ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);

            ns.attachAudio(mic);
            ns.publish(audioid, "record"); //發布到 FMS,並錄製
        }

 

        //錄音音量條的顯示,我實際的做法是在後面畫了的漸層,然後在前面根據Micphone的音量動態畫一個白色的遮罩

        private function initBackground() : void
        {
            var myMatrix:Matrix = new Matrix();
            trace("initBackground: " + myMatrix.toString()); // (a=1, b=0, c=0, d=1, tx=0, ty=0)
            
            myMatrix.createGradientBox(250, 250, 0, 50, 50);
            trace("initBackground: " + myMatrix.toString()); // (a=0.1220703125, b=0, c=0, d=0.1220703125, tx=150, ty=150)
            
            var colors:Array = [0x00FF00, 0xFFFF00, 0xFFFF00, 0xFF0000];
            var alphas:Array = [80, 100, 100, 100];
            var ratios:Array = [0, 175, 215, 0xFF];
            
            this.graphics.beginGradientFill(GradientType.LINEAR, colors, alphas, ratios, myMatrix);
            this.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
            this.graphics.endFill();
           
            this.graphHolder.graphics.beginFill(0xFFFFFF);
            this.graphHolder.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
            this.graphHolder.graphics.endFill();
        }

 

        private function drawMicLevel (e:TimerEvent) : void
        {
            if(!nc.connected) return;
           
            //ShowTrace("drawMicLevel, activityLevel: " + mic.activityLevel);
            var level = 100;
            this.graphHolder.graphics.clear();
            this.graphHolder.graphics.beginFill(0xFFFFFF);
            if(vuDirection == "horizontal")
            {
                level = (mic.activityLevel / 100) * stage.stageWidth;
                this.graphHolder.graphics.drawRect(level, 0, stage.stageWidth, stage.stageHeight);
            }
            else
            {
                level = (mic.activityLevel / 100) * stage.stageHeight;
                this.graphHolder.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight-level);
            }
           
            this.graphHolder.graphics.endFill();
            //ShowTrace("drawMicLevel, level: " + level);
        }

 

4. Flash 和Javascript 相互調用

 

        //Flash 調用 js 函數

        ExternalInterface.call("flash_callback_proxy", "failRecord");

 

        //Flash 中要允許 JavaScript 調用,並匯出相應的函數,這裡我是做了個代理,所有js調用都經過此函數轉寄

       {
            Security.allowDomain("*");
            Security.allowInsecureDomain("*");
            ExternalInterface.addCallback("js_callback_proxy", js_callback_proxy);

        }

        //function call by Javascript
        function js_callback_proxy(_p1, _p2) : Boolean
        {
            var ret : Boolean = true;
            ShowTrace("js_callback_proxy, cmd: " + _p1);
            switch(_p1){
                case "startRecord":
                    ret = startRecord(_p2);
                    break;
                case "stopRecord":
                    stopRecord();
                    break;
                case "setDirection":
                    setDirection(_p2);
                    break;
                case "sowPreference":
                    showParameters();
                    break;
                default:
                    ret = false;
            }
           
            return ret;
        }

 

        //js 中調用基本就是這樣

        obj.js_callback_proxy('startRecord', evalid);

 

       個人認為還是盡量不要從 js 中調用flash中的函數,一方面可能存在一些安全性方面的問題。另一方面我實際中也發現網頁中動態載入flash對象時,由js中調用flash函數會出現一些莫名其妙的問題。

 

5. 音頻格式的轉換


     這裡是直接採用 ffmpeg 以命令列的方式進行轉換,將 flv 轉換為 16K 16bit 單聲道 wav。

     ffmpeg -i d:/xxx.flv -ar 16000 -ab 16 -ac 1 d:/xxx.wav

 

6. 後續工作

 

     服務端採用流媒體即時解碼成 WAV,我看了一下 FMS 提供的 Server API,似乎並不支援即時擷取流媒體的資料。

     另一個好訊息是 Adobe 已經公開了 RTMP 協議的格式,而且我也看到了網上有別人寫好的支援此協議的開源服務端代碼,比如說 RED5,所以可以在開原始碼的基礎上進行開發。

 

    
參考網頁:List of Available RTMP Servers

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.