`
arm25arm
  • 浏览: 17992 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

Flash Media Server 应用笔记

阅读更多

Flash Media Server 应用笔记
2010年08月18日
  最近做的一个基于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_A dminGuide/WSE2A5A7B9-E118-496f-92F9-E295038DB7DB.ht ml 或 这里 。
  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.LINEA R, 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
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics