此篇為備忘錄
拿 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);
}