Skip to main content
Participant
July 23, 2010
Question

Server side stream caching

  • July 23, 2010
  • 2 replies
  • 1993 views

Hi,

I have a problem with server side caching on FMS (3.5.3).

I've taken the live main.asc and modified it in order to record streams on the server with timestamp names.

as follows:

/*
* application.onAppStart:
*                         is called when application load. It contains Live (out of the box)
* application specific initializations.
*/
load("AkamaiFCSPublish.asc");

application.onAppStart = function()
{
     ...
     trace("...loading completed.");
     application.streams = new Object();
     
}

...

/*
* 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});
     }*/
     var p_stream = Stream.get(streamname);     
     trace("FCPublish stream: "+p_stream.name);     
     
     td = new Date();
     var recordname = p_stream.name + "_" + td.getFullYear()+"-"+(td.getMonth()+1<10?"0":"")+(td.getMonth()+1)+"-"+(td.getDate()<10?"0":"")+td.getDate()+"_"+(td.getHours()<10?"0":"")+td.getHours()+"-"+(td.getMinutes()<10?"0":"")+td.getMinutes()+"-"+(td.getSeconds()<10?"0":"")+td.getSeconds();
     
     //Start recording
     if (application.streams[p_stream.name])
     {               
          s = Stream.get(application.streams[p_stream.name]);
          s.record(false);
          s = null;
     }
     application.streams[p_stream.name] = "mp4:"+recordname+".f4v";
     s = Stream.get(application.streams[p_stream.name]);
     if (s)
     {
          trace ("recording local file: "+application.streams[p_stream.name]);
          s.record();
          s.play(p_stream.name);
          s.onStatus = function (info){
               trace("streamrecinfo "+info.code+" "+info.description);
          }
     }     
}

/*
* 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});
     trace("onUnpublish stream: "+streamname);
     s = Stream.get(application.streams[streamname]);
     if (s)
     { 
          if (!s.record(false))
          {
               trace("Could not stop recording "+application.streams[streamname]);
          }
          else
               application.streams[streamname]= null;
          s = null;
     }          
     else
     {
          trace("Could not stop recording2 "+application.streams[streamname]);
     }
}

...

I use FMLE (3.1) to stream to the server and all works fine when i have only 1 stream going at one only time.

Now when i have several clients streaming to my server, the new client will stop the old client recording after a minute or so.

very strange.

I'm hoping someone could help me because this does not make sense. tracing doesnt even flag the fact that the stream is stopping recording.

Thank you

    This topic has been closed for replies.

    2 replies

    kaymystAuthor
    Participant
    July 26, 2010

    Thanks for this. it helped me correct my script. I'll post it here if it can help anybody.



    /*
    * 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.");
         application.streams = new Object();
         
    }

    /*
    *  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);
              if ( (pos > 0) && (pos  < domainEndPos) && (domainEndPos == (pos + patterns.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.
    */


    application.onConnect = function( p_client,p_autoSenseBW )
    {
         // 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);
         }
         
         // 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");
              
    }

    /*
    * 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});
    }

    application.onPublish = function (p_c, p_stream)
    {
         //p_stream.record();
         trace("onPublish stream: "+p_stream.name);     
         
         td = new Date();
         var recordname = p_stream.name + "_" + td.getFullYear()+"-"+(td.getMonth()+1<10?"0":"")+(td.getMonth()+1)+"-"+(td.getDate()<10?"0":"")+td.getDate()+"_"+(td.getHours()<10?"0":"")+td.getHours()+"-"+(td.getMinutes()<10?"0":"")+td.getMinutes()+"-"+(td.getSeconds()<10?"0":"")+td.getSeconds();
         
         //Start recording
         if (application.streams[p_stream.name])
         {               
              application.streams[p_stream.name].record(false);
              application.streams[p_stream.name] = null;
         }
         application.streams[p_stream.name] = Stream.get("mp4:"+recordname+".f4v");
         if (application.streams[p_stream.name])
         {
              trace ("recording local file: "+application.streams[p_stream.name].name);
              application.streams[p_stream.name].record();
              application.streams[p_stream.name].play(p_stream.name);
              application.streams[p_stream.name].onStatus = function (info){
                   trace("streamrecinfo "+info.code+" "+info.description);
              }
         }     
    }

    application.onUnpublish = function(p_c, p_stream)
    {
         trace("onUnpublish stream: "+p_stream.name);
         //p_stream.record(false);
         if (application.streams[p_stream.name])
         { 
              if (!application.streams[p_stream.name].record(false))
              {
                   trace("Could not stop recording "+application.streams[p_stream.name]);
              }
              application.streams[p_stream.name] = null;
         }          
         else
         {
              trace("Could not stop recording2 "+application.streams[p_stream.name].name);
         }
    }


    /*
    * 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 )
    {
         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 <space>, 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*$/, "");
    }

    Participating Frequently
    July 23, 2010

    I did not get in detail of your code but i guess your code might be the cause of the issue.

     application.streams[p_stream.name] = "mp4:"+recordname+".f4v";
         s = Stream.get(application.streams[p_stream.name]); 

    See above code is like whenever you publish new stream your "s" object will gets assigned new stream object thus stopping your older play and recording.

    I mean you can write very simple code using application.onPublish and application.onUnpublish.