此篇為備忘錄
拿 Flash Media Server 來做視頻串流 不外乎就可以做
錄影,與額外的規範
可使用的內建方法可參考官方
https://helpx.adobe.com/adobe-media-server/ssaslr/application-class.html
實際存放CODE的位置在
C:\Program Files (x86)\Adobe\Flash Media Server 4.5\applications\live
範例 CODE 如下
底下實作一個,當呼叫 rtmp URL後 做出相對應輸入參數的判斷邏輯
// mark - add
application.doCheckClient = function()
{
 var count = application.clients.length ;
 for(i = 0 ; i < count ; i++)
 {
  var target = application.clients[i] ;
  var server_id   = target.server_id ;
  var no_run      = target.no_run ;
  var no_active   = target.no_active ;
  var game_status = target.game_status ;
  var check_value = no_run + "," + no_active + "," + game_status ;
     
     if(check_value != application.src_list[server_id])
     {
      trace("loop-stop-it") ;
      application.disconnect(application.clients[i]) ;
     }
     else
     {
      trace("loop-it") ;
     }
 }
}
/* 
* application.onAppStart:
*     is called when application load. It contains Live (out of the box) 
* application specific initializations. 
*/ 
application.onAppStart = function()
{
 // Logging
 trace("Starting Live Service...");
 //  Turning on the Authentication by default
 this.HTMLDomainsAuth = true;
 this.SWFDomainsAuth = true;
 
 // Populating the list of domains which are allowed to host HTML file
 // which in turn may embed a SWF that connects to this application
 this.allowedHTMLDomains = this.readValidDomains("allowedHTMLdomains.txt","HTMLDomains");
 // Populating the list of domains which are allowed to host a SWF file
 // which may connect to this application
 this.allowedSWFDomains = this.readValidDomains("allowedSWFdomains.txt","SWFDomains");
 
 // Logging
 if(this.HTMLDomainsAuth){
  trace("Authentication of HTML page URL domains is enabled");
 }
 if(this.SWFDomainsAuth){
  trace("Authentication of SWF URL domains is enabled");
 }
 trace("...loading completed.");
    // mark - add - start
 application.src_list = Array() ;
 setInterval(this.doCheckClient , 2000) ;
 // mark - add - end
}
/*
*  application.validate:
*     function to validate a given URL by matching through a list of
* allowed patterns.
* 
* Parameters:
*  url:  contains the input url string.
*  patterns: Array; an array of permmited url patterns.
* 
* return value:
*    true; when 'url domain" contains a listed domains as a suffix.
*     false; otherwise.
*/
application.validate = function( url, patterns )
{
 // Convert to lower case
 url = url.toLowerCase();
 var domainStartPos = 0; // domain start position in the URL
 var domainEndPos = 0; // domain end position in the URL
 
 switch (url.indexOf( "://" ))
 {
  case 4:
   if(url.indexOf( "http://" ) ==0)
    domainStartPos = 7;
   break;
  case 5:
   if(url.indexOf( "https://" ) ==0)
    domainStartPos = 8;
   break;
 }
 if(domainStartPos == 0)
 {
  // URL must be HTTP or HTTPS protocol based
  return false;
 }
 domainEndPos = url.indexOf("/", domainStartPos);
 if(domainEndPos>0)
 {
  colonPos = url.indexOf(":", domainStartPos); 
  if( (colonPos>0) && (domainEndPos > colonPos))
  {
   // probably URL contains a port number
   domainEndPos = colonPos; // truncate the port number in the URL
  }
 }
 for ( var i = 0; i < patterns.length; i++ )
 {
  var pos = url.lastIndexOf( patterns[i]);
  if ( (pos > 0) && (pos  < domainEndPos) && (domainEndPos == (pos + patterns[i].length)) )
   return true;
 }
 return false;
}
/*
*  application.onConnect:
*     Implementation of the onConnect interface function (optional). 
*  it is invoked whenever a client connection request connection. Live app uses this 
*  function to authenticate the domain of connection and authorizes only 
*  for a subscriber request.
*/ 
// 用戶端連線
// 取得GET參數
// (1)轉換成serverid,norun,noactive,gamestatus [或其他]
// (2)呼叫取得gs狀態api [或其他]
// 比對(1)和(2)的結果字串:不一致則斷線
// [todo?] 每N分鐘重新檢查
application.onConnect = function( p_client, p_autoSenseBW )
{
  // mark - add - start
  trace("client-uri=" + p_client.uri) ;
  var uri         = p_client.uri ;
  var start_index = uri.indexOf("?") + 1 ;
  
  var info        = "" ;
  if(start_index != 0)
  {
     info     = uri.slice(start_index) ;
     info     = info.split("=")[1] ;
     
     sub_info = info.split(",") ;
     p_client.server_id   = sub_info[0] ;
     p_client.no_run      = sub_info[1] ;
     p_client.no_active   = sub_info[2] ;
     p_client.game_status = sub_info[3] ;
  }
  else
  {
     p_client.server_id   = "" ;
     p_client.no_run      = "" ;
     p_client.no_active   = "" ;
     p_client.game_status = "" ;
  }
  
  trace("info        = " + info) ;
  trace("server_id   = " + p_client.server_id) ;
  trace("no_run      = " + p_client.no_run) ;
  trace("no_active   = " + p_client.no_active) ;
  trace("game_status = " + p_client.game_status) ;
  
  var api_loader = new LoadVars(); 
  api_loader.onData = function(src) 
  {
    trace("api_loader.onData-start") ;
    
    if (src == undefined) 
    {
        trace("Error loading content.") ;
        return false ;
    }
    // 0604250002,011118022,1182,4
    var check_value = p_client.no_run + "," + p_client.no_active + "," + p_client.game_status ;
    trace("check_value = " + check_value) ;
    trace("src = " + src) ;
    application.src_list[p_client.server_id] = src ;
//  trace("show====" + application.src_list[p_client.server_id]) ;
    if(src == check_value)
    {
     trace("client pass") ;
    }
    else
    {
     trace("client cant pass") ;
     application.disconnect(p_client) ;
     return false ;
    }
  } ;
  api_loader.decode("serverid="+p_client.server_id) ;
  
  // http://ddd.bacc1688.com:8933/GetServerInfo.aspx?serverid=0604250002
  var url = "http://ddd.bacc1688.com:8933/GetServerInfo.aspx" ;
  
  trace("api url = " + url) ;
  
  api_loader.load(url) ; 
  // mark - add - end
 // Check if pageUrl is from a domain we know. 
 // Check pageurl
 // A request from Flash Media Encoder is not checked for authentication
 if( (p_client.agent.indexOf("FME")==-1) && (p_client.agent.indexOf("FMLE")==-1))
 {
  // Authenticating HTML file's domain for the request :
  // Don't call validate() when the request is from localhost 
  // or HTML Domains Authentication is off.
  if ((p_client.ip != "127.0.0.1") && application.HTMLDomainsAuth 
    &&  !this.validate( p_client.pageUrl, this.allowedHTMLDomains ) )
  {
   trace("Authentication failed for pageurl: " + p_client.pageUrl + ", rejecting connection from "+p_client.ip);
   return false;
  }
 
  // Authenticating the SWF file's domain for the request :
  // Don't call validate() when the request is from localhost 
  // or SWF Domains Authentication is off.
  if ((p_client.ip != "127.0.0.1") && application.SWFDomainsAuth 
    &&  !this.validate( p_client.referrer, this.allowedSWFDomains ) )
  {
   trace("Authentication failed for referrer: " + p_client.referrer + ", rejecting connection from "+p_client.ip);
   return false;
  }
   // Logging
  trace("Accepted a connection from IP:"+ p_client.ip 
      + ", referrer: "+ p_client.referrer
      + ", pageurl: "+ p_client.pageUrl);
 }else{
  // Logging
  trace("Adobe Flash Media Encoder connected from "+p_client.ip);
  this.acceptConnection(p_client);
 }
 
 // As default, all clients are disabled to access raw audio and video and data bytes in a stream 
 // through the use of BitmapData.draw() and SoundMixer.computeSpectrum()., Please refer
 // Stream Data Access doccumentations to know flash player version requirement to support this restriction
 // Access permissions can be allowed for all by uncommenting the following statements
 
 //p_client.audioSampleAccess = "/";
  //p_client.videoSampleAccess = "/"; 
 this.acceptConnection(p_client);
  
 // A connection from Flash 8 & 9 FLV Playback component based client 
 // requires the following code.
 if (p_autoSenseBW)
  p_client.checkBandwidth();
 else
  p_client.call("onBWDone");
  
 trace(" @@@ onconnect-end @@@ ");
}
application.onStatus = function(info){ 
    trace("code: " + info.code + " level: " + info.level); 
    trace(info.code + " details: " + info.details); 
}; 
/*
* Client.prototype.getPageUrl
*     Public API to return URL of the HTML page.    
* 
*/
Client.prototype.getPageUrl = function() {
 return this.pageUrl;
}
/*
* Client.prototype.getReferrer
*     Public API to return Domain URL of the client SWF file.    
* 
*/
Client.prototype.getReferrer = function() {
 return this.referrer;
}
/*
* FCPublish :
* FME calls FCPublish with the name of the stream whenever a new stream 
* is published. This notification can be used by server-side action script
* to maintain list of all streams or also to force FME to stop publishing.
* To stop publishing, call "onFCPublish" with an info object with status
* code set to "NetStream.Publish.BadName".
*/ 
Client.prototype.FCPublish = function( streamname )
{
 // setup your stream and check if you want to allow this stream to be published
 if ( true) // do some validation here
 {      // this is optional.
  this.call("onFCPublish", null, {code:"NetStream.Publish.Start", description:streamname});
 }
 else
 {
  this.call("onFCPublish", null, {code:"NetStream.Publish.BadName", description:streamname});
 }
  
}
/*
* FCUnpublish :
* FME notifies server script when a stream is unpublished.
*/
Client.prototype.FCUnpublish = function( streamname )
{
 // perform your clean  up
    this.call("onFCUnpublish", null, {code:"NetStream.Unpublish.Success", description:streamname});
}
/*
* releaseStream :
* When FME connection to FMS drops during a publishing session it will
* try and republish the stream when connection is restored. On certain
* occasions FMS will reject the new stream because server is still 
* unaware of the connection drop, sometimes this can take a few minutes.
* FME calls "releaseStream" method with the stream name and this can be
* used to forcibly clear the stream.
*/ 
Client.prototype.releaseStream = function(streamname)
{
  s = Stream.get(streamname);
  s.play(false);
}
/*
* application.readValidDomains
*    Function to read Allowed domain file 
* Parameters:
*   fileName:
*    name of the file in the application directory
* which contains one valid domain name per line. This file can contain
* comments followed by a '#' as the very first charector in that line. 
* a non-comment entry with a space is considered as an error case.
*  
* returns
*   an array in which each entry contains a domain name 
* listed in the file.
*/
application.readValidDomains = function( fileName , domainsType )
{
trace("filename="+fileName);
 var domainFile = new File(fileName);
 var domainsArray = new Array();
 var index = 0;
 var lineCount = 0;
 var tempLine;
 domainFile.open("text", "read"); 
 
 // Read the file line-by-line and fill the domainsArray 
 // with valid entries
 while (domainFile.isOpen && ! domainFile.eof() )
 {
  
  tempLine = domainFile.readln();
  lineCount++;
  if( !tempLine  || tempLine.indexOf("#") == 0)
  {
   continue;
  }
  tempLine = tempLine.trim();
  if(tempLine.indexOf(" ")!=-1)
  {
   trace("undesired , domain entry ignored. "+fileName+":"+(lineCount+1));
  }
  else
  {
   domainsArray[index] =  tempLine.toLowerCase();
   index++;
   
   if(tempLine == "*")
   {
    switch (domainsType){
     
     case "HTMLDomains":
      trace ("Found wildcard (*) entry: disabling authentication for HTML file domains ") ;
      application.HTMLDomainsAuth = false;  
      break;
     
     case "SWFDomains":
      trace ("Found wildcard (*) entry: disabling authentication for SWF file domains ") ;
      this.SWFDomainsAuth = false;  
      break;
      
     default:
      // Do nothing
      break; 
    }
   }
  } 
 } // End while
 
 // Something is wrong! the domains file must be accessible.
 if( !domainFile.isOpen){
  trace("Error: could not open '"+fileName+"', rejecting all clients except localhost. ");
  
 }
 else 
 {
  domainFile.close();
 }
 return domainsArray;
}
/**
* String.prototype.trim:
*    Function to trim spaces in start an end of an input string.
* returns:
*   a trimmed string without any leading & ending spaces.
*    
*/
String.prototype.trim = function () {
 
 return this.replace(/^\s*/, "").replace(/\s*$/, "");
}
application.onPublish = function(client, stream){
 trace("onPublish.client.uri=" + client.uri);
}