织梦CMS - 轻松建站从此开始!

罗索

Flash Media Server 应用笔记

落鹤生 发布于 2011-02-18 21:37 点击:次 
最近做的一个基于FMS的Flash录音演示,主要的几个技术点:1. FMS 安装及使用;2. Flash 录音及相应的安全性问题;3. 声音文件的转换。
TAG:

最近做的一个基于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

(jh_zzz)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201102/10922.html]
本文出处:CSDN博客 作者:jh_zzz
顶一下
(2)
66.7%
踩一下
(1)
33.3%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容