/**
  *
  * @copyright Copyright 2001-2008 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @affects lzpreloader
  * @access private
  * @topic LZX
  * @subtopic Services
  */

//
// Code for lzpreloader frame 0 when there's a <splash> tag
// This is compiled into lzpreloader.lzx which is attached to the lzpreloader
// movieclip in SWFFile.java addPreloaderFrame()
//

// Debugging tips :
// I put in a little debug code that will enables putting simple debug calls
// in the code.  To see the output when the debugger is up, type:
//          lzpreloader.trace()
// It would be nice, for it to automatically output this info to the debugger
// when it starts up, but I think this file isn't compiled the same way as the
// LFC, since the $debug didn't seem to work.  For now, you can just uncomment
// the debug code below. [Sarah Allen 04-28-05]
// Added text field where debug info is shown so it can be seen with debug=true
// [sallen 06-06-2005]

// necessary for consistent behavior - in netscape browsers HTML is ignored
Stage.align = ('canvassalign' in global && global.canvassalign != null) ? global.canvassalign : "LT";
Stage.scaleMode = ('canvasscale' in global && global.canvasscale != null) ? global.canvasscale : "noScale";

/*
// SWF-specific
_root.createTextField("tfNewfield",1,100,10,150,700);
_root.tfNewfield.text = "Splash Debugger";
_root.tfNewfield.autoSize = true;

   mydebugger = [];
   function mydebug(s) {
        this.mydebugger.push(s);
        _root.tfNewfield.text = _root.tfNewfield.text + '\n' + s;
   }
   function trace() {
        for (var i=0; i< this.mydebugger.length; i++) {
            _root.Debug.write(this.mydebugger[i]);
        }
   }
*/

this.protoviews = [];
this.synctoloads = [];
this._x = 0;
this._y = 0;
this._lastwidth = Stage.width;
this._lastheight = Stage.height;

// called by compiler (first thing) like:
// lzpreloader.create({attrs: {}, name: "splash", children:
//   [{attrs: {x: 100, name: "logo", synctoload: true, resourcename:
//   logo, src: "logo.swf", y: 100}, name: "preloadresource"}]});
function create (iobj) {
  this.iobj = iobj
}

// Called by heartbeat to actually build the splash once the Stage has
// non-zero size (to work around ActiveX control bug that initializes
// the Stage to 0x0).
function init () {
  if (Stage.width <= 100 || Stage.height <= 100) {
    // sometimes Safari still has heihgt/width 100.  Be sure to hide the splash if this is the case.
    this.hideafterinit = true;
    return;
  }
  var iobj = this.iobj;
  delete this.iobj;
  this.name = iobj.name;
  this.hideafterinit = iobj.attrs.hideafterinit;
  var viewprops = {x: true, y: true, width: true, height: true};
  var sr = _root.createEmptyMovieClip("spriteroot", 1000);
  var root = sr.createEmptyMovieClip(this.name, 0);
  this.splashroot = root;
  //this.mydebug('new root' + this.splashroot);
  for (var i = 0; i < iobj.children.length; i++) {
    var c = iobj.children[i].attrs;
    var n = (c.name == null) ? ("child" + i) : c.name;
    root.attachMovie(n, n, i + 1);
    var mc = root[n];
    mc.name = n;
    this.protoviews.push(mc);

//    this.debug(" synctoload" + c.synctoload);
    if (c.synctoload) {
      if (c.lastframe == null) {
        mc.lastframe = mc._totalframes;
      } else {
        //this.mydebug('c.usepercent='+c.usepercent);
        mc.lastframe = c.lastframe * mc._totalframes;
        //this.mydebug('c.lastframe='+c.lastframe+
        //    'mc.lastframe='+mc.lastframe );
      }
//      this.debug(" synctoload" + this.synctoloads);
      this.synctoloads.push(mc);
    }
    for (var p in viewprops) {
      if (c[p] != null) {
        mc["_" + p] = c[p];
      }
    }

    //this.debug(mc._target + " center=" + c.center);
    if (c.center != null) {
      mc._x = Stage.width / 2 - (mc._width / 2);
      mc._y = Stage.height / 2 - (mc._height / 2);
      //this.debug(" Stage.width=" + Stage.width);
      //this.debug(" mc._width=" + mc._width);
      //this.debug(" mc._x=" + mc._x);
    }
  }
}

this.createEmptyMovieClip("_heartbeat", 1000);
this._heartbeat.onEnterFrame = function() {
    this._parent.heartbeat();
}

// called by enterframe of the splash clip
function heartbeat (p) {
  //this.mydebug('stage size ' + Stage.width + 'x' + Stage.height );
  if (this.iobj && (Stage.width > 100 && Stage.height > 100) &&
        (Stage.width == this._lastwidth && Stage.height == this._lastheight)) {
        // some browsers will pass 100% as 100 (or whatever %) for first
        // couple of heartbeats, ideally we would pass % from compiler
        // so we could ignore fewer cases and allow splash in small canvas
        // [sallen/max 6-05]
    this.init();
    mc.gotoAndStop(0);
  } else {
    // SWF-specific
    var percload = _root.getBytesLoaded() / _root.getBytesTotal();
    var id = _root.id;
    if (id) {
        var js = 'if (window.lz && lz.embed && lz.embed.applications && lz.embed.applications.' + id + ') lz.embed.applications.' + id + '._sendPercLoad(' + Math.floor(percload * 100) + ')';
        _root.getURL('javascript:' + js + ';void(0);');
    }

    for (var i = 0; i < this.synctoloads.length; i++) {
        var mc = this.synctoloads[i];
        var p = Math.floor(mc.lastframe * percload);
        mc.gotoAndStop(p);
    }
    //this.mydebug('percent done='+p);
  }
  this._lastwidth = Stage.width;
  this._lastheight = Stage.height;
}

// called by the compiler (as the last instruction in the executable),
// this causes the transition from loading animation to construction
// animation.
function done() {
  //this.mydebug('done');
  lzpreloader._heartbeat.removeMovieClip();
  delete this.heartbeat;

  if (this.iobj) this.init();
  for (var i = 0; i < this.synctoloads.length; i++) {
    var mc = this.synctoloads[i];
    mc.gotoAndStop(mc.lastframe);
    //this.mydebug('lastframe='+mc.lastframe);
  }
  var vis = (this.hideafterinit != true) ? true : false;

  var svd = canvas.sprite.__LZsvdepth;
  canvas.sprite.__LZsvdepth = 0;
  var v = new LzView(canvas, {name:this.name, visible:vis,
                              options: {ignorelayout:true}});
  v.sprite.setMovieClip(this.splashroot);
  canvas.sprite.__LZsvdepth = svd;
  //this.mydebug('splashmc='+v);

  for (var i = 0; i < this.protoviews.length; i++) {
    var mc = this.protoviews[i];
    var ratio = mc.lastframe / mc._totalframes;
    var nv = new LzView(v, {name: mc.name, x: mc._x, y: mc._y,
                                  width: mc._width, height: mc._height,
                                  totalframes: mc._totalframes, ratio: ratio,
                                  options: {ignorelayout: true}});
    //this.mydebug('new view='+nv);
    // If ratio is less than one, animate the remaining way using percentcreated
    if (ratio < 1) {
      nv.noteCreated = function (p) {
        this.stop(Math.floor(this.totalframes * (this.ratio + (1-this.ratio)*p)));
      };
      var del = new _root.LzDelegate(nv, 'noteCreated', _root.canvas, 'onpercentcreated');
    }
    nv.sprite.setMovieClip(mc);
    mc._visible = true;
  }

}

/**
  *
  * @copyright Copyright 2001-2008 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @affects lzpreloader
  * @access private
  * @topic LZX
  * @subtopic Services
  */

//
// Code for lzpreloader frame 0 when there's a <splash> tag
// This is compiled into lzpreloader.lzx which is attached to the lzpreloader
// movieclip in SWFFile.java addPreloaderFrame()
//

// Debugging tips :
// I put in a little debug code that will enables putting simple debug calls
// in the code.  To see the output when the debugger is up, type:
//          lzpreloader.trace()
// It would be nice, for it to automatically output this info to the debugger
// when it starts up, but I think this file isn't compiled the same way as the
// LFC, since the $debug didn't seem to work.  For now, you can just uncomment
// the debug code below. [Sarah Allen 04-28-05]
// Added text field where debug info is shown so it can be seen with debug=true
// [sallen 06-06-2005]

// necessary for consistent behavior - in netscape browsers HTML is ignored
Stage.align = ('canvassalign' in global && global.canvassalign != null) ? global.canvassalign : "LT";
Stage.scaleMode = ('canvasscale' in global && global.canvasscale != null) ? global.canvasscale : "noScale";

/*
// SWF-specific
_root.createTextField("tfNewfield",1,100,10,150,700);
_root.tfNewfield.text = "Splash Debugger";
_root.tfNewfield.autoSize = true;

   mydebugger = [];
   function mydebug(s) {
        this.mydebugger.push(s);
        _root.tfNewfield.text = _root.tfNewfield.text + '\n' + s;
   }
   function trace() {
        for (var i=0; i< this.mydebugger.length; i++) {
            _root.Debug.write(this.mydebugger[i]);
        }
   }
*/

this.protoviews = [];
this.synctoloads = [];
this._x = 0;
this._y = 0;
this._lastwidth = Stage.width;
this._lastheight = Stage.height;

// called by compiler (first thing) like:
// lzpreloader.create({attrs: {}, name: "splash", children:
//   [{attrs: {x: 100, name: "logo", synctoload: true, resourcename:
//   logo, src: "logo.swf", y: 100}, name: "preloadresource"}]});
function create (iobj) {
  this.iobj = iobj
}

// Called by heartbeat to actually build the splash once the Stage has
// non-zero size (to work around ActiveX control bug that initializes
// the Stage to 0x0).
function init () {
  if (Stage.width <= 100 || Stage.height <= 100) {
    // sometimes Safari still has heihgt/width 100.  Be sure to hide the splash if this is the case.
    this.hideafterinit = true;
    return;
  }
  var iobj = this.iobj;
  delete this.iobj;
  this.name = iobj.name;
  this.hideafterinit = iobj.attrs.hideafterinit;
  var viewprops = {x: true, y: true, width: true, height: true};
  var sr = _root.createEmptyMovieClip("spriteroot", 1000);
  var root = sr.createEmptyMovieClip(this.name, 0);
  this.splashroot = root;
  //this.mydebug('new root' + this.splashroot);
  for (var i = 0; i < iobj.children.length; i++) {
    var c = iobj.children[i].attrs;
    var n = (c.name == null) ? ("child" + i) : c.name;
    root.attachMovie(n, n, i + 1);
    var mc = root[n];
    mc.name = n;
    this.protoviews.push(mc);

//    this.debug(" synctoload" + c.synctoload);
    if (c.synctoload) {
      if (c.lastframe == null) {
        mc.lastframe = mc._totalframes;
      } else {
        //this.mydebug('c.usepercent='+c.usepercent);
        mc.lastframe = c.lastframe * mc._totalframes;
        //this.mydebug('c.lastframe='+c.lastframe+
        //    'mc.lastframe='+mc.lastframe );
      }
//      this.debug(" synctoload" + this.synctoloads);
      this.synctoloads.push(mc);
    }
    for (var p in viewprops) {
      if (c[p] != null) {
        mc["_" + p] = c[p];
      }
    }

    //this.debug(mc._target + " center=" + c.center);
    if (c.center != null) {
      mc._x = Stage.width / 2 - (mc._width / 2);
      mc._y = Stage.height / 2 - (mc._height / 2);
      //this.debug(" Stage.width=" + Stage.width);
      //this.debug(" mc._width=" + mc._width);
      //this.debug(" mc._x=" + mc._x);
    }
  }
}

this.createEmptyMovieClip("_heartbeat", 1000);
this._heartbeat.onEnterFrame = function() {
    this._parent.heartbeat();
}

// called by enterframe of the splash clip
function heartbeat (p) {
  //this.mydebug('stage size ' + Stage.width + 'x' + Stage.height );
  if (this.iobj && (Stage.width > 100 && Stage.height > 100) &&
        (Stage.width == this._lastwidth && Stage.height == this._lastheight)) {
        // some browsers will pass 100% as 100 (or whatever %) for first
        // couple of heartbeats, ideally we would pass % from compiler
        // so we could ignore fewer cases and allow splash in small canvas
        // [sallen/max 6-05]
    this.init();
    mc.gotoAndStop(0);
  } else {
    // SWF-specific
    var percload = _root.getBytesLoaded() / _root.getBytesTotal();
    var id = _root.id;
    if (id) {
        var js = 'if (window.lz && lz.embed && lz.embed.applications && lz.embed.applications.' + id + ') lz.embed.applications.' + id + '._sendPercLoad(' + Math.floor(percload * 100) + ')';
        _root.getURL('javascript:' + js + ';void(0);');
    }

    for (var i = 0; i < this.synctoloads.length; i++) {
        var mc = this.synctoloads[i];
        var p = Math.floor(mc.lastframe * percload);
        mc.gotoAndStop(p);
    }
    //this.mydebug('percent done='+p);
  }
  this._lastwidth = Stage.width;
  this._lastheight = Stage.height;
}

// called by the compiler (as the last instruction in the executable),
// this causes the transition from loading animation to construction
// animation.
function done() {
  //this.mydebug('done');
  lzpreloader._heartbeat.removeMovieClip();
  delete this.heartbeat;

  if (this.iobj) this.init();
  for (var i = 0; i < this.synctoloads.length; i++) {
    var mc = this.synctoloads[i];
    mc.gotoAndStop(mc.lastframe);
    //this.mydebug('lastframe='+mc.lastframe);
  }
  var vis = (this.hideafterinit != true) ? true : false;

  var svd = canvas.sprite.__LZsvdepth;
  canvas.sprite.__LZsvdepth = 0;
  var v = new LzView(canvas, {name:this.name || null, visible:vis,
                              options: {ignorelayout:true}});
  v.sprite.setMovieClip(this.splashroot);
  canvas.sprite.__LZsvdepth = svd;
  //this.mydebug('splashmc='+v);

  for (var i = 0; i < this.protoviews.length; i++) {
    var mc = this.protoviews[i];
    var ratio = mc.lastframe / mc._totalframes;
    var nv = new LzView(v, {name: mc.name || null, x: mc._x, y: mc._y,
                                  width: mc._width, height: mc._height,
                                  totalframes: mc._totalframes, ratio: ratio,
                                  options: {ignorelayout: true}});
    //this.mydebug('new view='+nv);
    // If ratio is less than one, animate the remaining way using percentcreated
    if (ratio < 1) {
      nv.noteCreated = function (p) {
        this.stop(Math.floor(this.totalframes * (this.ratio + (1-this.ratio)*p)));
      };
      var del = new _root.LzDelegate(nv, 'noteCreated', _root.canvas, 'onpercentcreated');
    }
    nv.sprite.setMovieClip(mc);
    mc._visible = true;
  }

}

Change 20081212-maxcarlson-l by maxcarlson@Bank.lan on 2008-12-12 22:46:35 PST
    in /Users/maxcarlson/openlaszlo/trunk-clean
    for http://svn.openlaszlo.org/openlaszlo/trunk

Summary: Prevent warnings for splash in swf8

Bugs Fixed: LPP-7458 - runtime errors when spash view included in nightly build 12036 swf8

Technical Reviewer: bargull@openlaszlo.org
QA Reviewer: promanik

Details: Per Andre's comment, make sure name is null instead of undefined when undeclared.

Tests: Both my-apps/copy-of-hello.lzx?lzr=swf8&debug=true and
<canvas>
    <splash/>
</canvas>

no longer give warnings.

git-svn-id: d62bde4b5aa582fdf07c8d403e53e0399a7ed285@12096 fa20e4f9-1d0a-0410-b5f3-dc9c16b8b17c


package org.mangui.hls.demux {
    import org.mangui.hls.HLSAudioTrack;
	import org.mangui.hls.flv.FLVTag;

    import flash.utils.ByteArray;

    CONFIG::LOGGING {
    import org.mangui.hls.utils.Log;
    }

    /** Constants and utilities for the AAC audio format. **/
    public class AACDemuxer implements Demuxer {
        /** ADTS Syncword (111111111111), ID (MPEG4), layer (00) and protection_absent (1).**/
        private static const SYNCWORD : uint = 0xFFF1;
        /** ADTS Syncword with MPEG2 stream ID (used by e.g. Squeeze 7). **/
        private static const SYNCWORD_2 : uint = 0xFFF9;
        /** ADTS Syncword with MPEG2 stream ID (used by e.g. Envivio 4Caster). **/
        private static const SYNCWORD_3 : uint = 0xFFF8;
        /** ADTS/ADIF sample rates index. **/
        private static const RATES : Array = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
        /** ADIF profile index (ADTS doesn't have Null). **/
        private static const PROFILES : Array = ['Null', 'Main', 'LC', 'SSR', 'LTP', 'SBR'];
        /** Byte data to be read **/
        private var _data : ByteArray;
        /* callback functions for audio selection, and parsing progress/complete */
        private var _callback_audioselect : Function;
        private var _callback_progress : Function;
        private var _callback_complete : Function;

        /** append new data */
        public function append(data : ByteArray) : void {
            _data.writeBytes(data);
        }

        /** cancel demux operation */
        public function cancel() : void {
            _data = null;
        }

        public function notifycomplete() : void {
            CONFIG::LOGGING {
            Log.debug("AAC: extracting AAC tags");
            }
            var audioTags : Vector.<FLVTag> = new Vector.<FLVTag>();
            /* parse AAC, convert Elementary Streams to TAG */
            _data.position = 0;
            var id3 : ID3 = new ID3(_data);
            // AAC should contain ID3 tag filled with a timestamp
            var frames : Vector.<AudioFrame> = AACDemuxer.getFrames(_data, _data.position);
            var adif : ByteArray = getADIF(_data, 0);
            var adifTag : FLVTag = new FLVTag(FLVTag.AAC_HEADER, id3.timestamp, id3.timestamp, true);
            adifTag.push(adif, 0, adif.length);
            audioTags.push(adifTag);

            var audioTag : FLVTag;
            var stamp : uint;
            var i : int = 0;

            while (i < frames.length) {
                stamp = Math.round(id3.timestamp + i * 1024 * 1000 / frames[i].rate);
                audioTag = new FLVTag(FLVTag.AAC_RAW, stamp, stamp, false);
                if (i != frames.length - 1) {
                    audioTag.push(_data, frames[i].start, frames[i].length);
                } else {
                    audioTag.push(_data, frames[i].start, _data.length - frames[i].start);
                }
                audioTags.push(audioTag);
                i++;
            }
            var audiotracks : Vector.<HLSAudioTrack> = new Vector.<HLSAudioTrack>();
            audiotracks.push(new HLSAudioTrack('AAC ES', HLSAudioTrack.FROM_DEMUX, 0, true));
            // report unique audio track. dont check return value as obviously the track will be selected
            _callback_audioselect(audiotracks);
            CONFIG::LOGGING {
            Log.debug("AAC: all tags extracted, callback demux");
            }
            _callback_progress(audioTags);
            _callback_complete();
        }

        public function AACDemuxer(callback_audioselect : Function, callback_progress : Function, callback_complete : Function) : void {
            _callback_audioselect = callback_audioselect;
            _callback_progress = callback_progress;
            _callback_complete = callback_complete;
            _data = new ByteArray();
        };

        public static function probe(data : ByteArray) : Boolean {
            var pos : uint = data.position;
            var id3 : ID3 = new ID3(data);
            // AAC should contain ID3 tag filled with a timestamp
            if (id3.hasTimestamp) {
                while (data.bytesAvailable > 1) {
                    // Check for ADTS header
                    var short : uint = data.readUnsignedShort();
                    if (short == SYNCWORD || short == SYNCWORD_2 || short == SYNCWORD_3) {
                        // rewind to sync word
                        data.position -= 2;
                        return true;
                    } else {
                        data.position--;
                    }
                }
                data.position = pos;
            }
            return false;
        }

        /** Get ADIF header from ADTS stream. **/
        public static function getADIF(adts : ByteArray, position : uint) : ByteArray {
            adts.position = position;
            var short : uint;
            // we need at least 6 bytes, 2 for sync word, 4 for frame length
            while ((adts.bytesAvailable > 5) && (short != SYNCWORD) && (short != SYNCWORD_2) && (short != SYNCWORD_3)) {
                short = adts.readUnsignedShort();
            }

            if (short == SYNCWORD || short == SYNCWORD_2 || short == SYNCWORD_3) {
                var profile : uint = (adts.readByte() & 0xF0) >> 6;
                // Correcting zero-index of ADIF and Flash playing only LC/HE.
                if (profile > 3) {
                    profile = 5;
                } else {
                    profile = 2;
                }
                adts.position--;
                var srate : uint = (adts.readByte() & 0x3C) >> 2;
                adts.position--;
                var channels : uint = (adts.readShort() & 0x01C0) >> 6;
            } else {
                throw new Error("Stream did not start with ADTS header.");
            }
            // 5 bits profile + 4 bits samplerate + 4 bits channels.
            var adif : ByteArray = new ByteArray();
            adif.writeByte((profile << 3) + (srate >> 1));
            adif.writeByte((srate << 7) + (channels << 3));
            CONFIG::LOGGING {
            Log.debug('AAC: ' + PROFILES[profile] + ', ' + RATES[srate] + ' Hz ' + channels + ' channel(s)');
            }
            // Reset position and return adif.
            adts.position -= 4;
            adif.position = 0;
            return adif;
        };

        /** Get a list with AAC frames from ADTS stream. **/
        public static function getFrames(adts : ByteArray, position : uint) : Vector.<AudioFrame> {
            var frames : Vector.<AudioFrame> = new Vector.<AudioFrame>();
            var frame_start : uint;
            var frame_length : uint;
            var id3 : ID3 = new ID3(adts);
            position += id3.len;
            // Get raw AAC frames from audio stream.
            adts.position = position;
            var samplerate : uint;
            // we need at least 6 bytes, 2 for sync word, 4 for frame length
            while (adts.bytesAvailable > 5) {
                // Check for ADTS header
                var short : uint = adts.readUnsignedShort();
                if (short == SYNCWORD || short == SYNCWORD_2 || short == SYNCWORD_3) {
                    // Store samplerate for offsetting timestamps.
                    if (!samplerate) {
                        samplerate = RATES[(adts.readByte() & 0x3C) >> 2];
                        adts.position--;
                    }
                    // Store raw AAC preceding this header.
                    if (frame_start) {
                        frames.push(new AudioFrame(frame_start, frame_length, frame_length, samplerate));
                    }
                    if (short == SYNCWORD_3) {
                        // ADTS header is 9 bytes.
                        frame_length = ((adts.readUnsignedInt() & 0x0003FFE0) >> 5) - 9;
                        frame_start = adts.position + 3;
                        adts.position += frame_length + 3;
                    } else {
                        // ADTS header is 7 bytes.
                        frame_length = ((adts.readUnsignedInt() & 0x0003FFE0) >> 5) - 7;
                        frame_start = adts.position + 1;
                        adts.position += frame_length + 1;
                    }
                } else {
                    CONFIG::LOGGING {
                    Log.debug("no ADTS header found, probing...");
                    }
                    adts.position--;
                }
            }
            if (frame_start) {
                // check if we have a complete frame available at the end, i.e. last found frame is fitting in this PES packet
                var overflow : int = frame_start + frame_length - adts.length;
                if (overflow <= 0 ) {
                    // no overflow, Write raw AAC after last header.
                    frames.push(new AudioFrame(frame_start, frame_length, frame_length, samplerate));
                } else {
                    CONFIG::LOGGING {
                    Log.debug2("ADTS overflow at the end of PES packet, missing " + overflow + " bytes to complete the ADTS frame");
                    }
                }
            }
            else if (frames.length == 0) {
                null;
                CONFIG::LOGGING {
                Log.warn("No ADTS headers found in this stream.");
                }
            }
            adts.position = position;
            return frames;
        };
    }
}

package org.mangui.hls.demux {
    import org.mangui.hls.HLSAudioTrack;
	import org.mangui.hls.flv.FLVTag;

    import flash.utils.ByteArray;

    CONFIG::LOGGING {
    import org.mangui.hls.utils.Log;
    }

    /** Constants and utilities for the AAC audio format. **/
    public class AACDemuxer implements Demuxer {
        /** ADTS Syncword (111111111111), ID (MPEG4), layer (00) and protection_absent (1).**/
        private static const SYNCWORD : uint = 0xFFF1;
        /** ADTS Syncword with MPEG2 stream ID (used by e.g. Squeeze 7). **/
        private static const SYNCWORD_2 : uint = 0xFFF9;
        /** ADTS Syncword with MPEG2 stream ID (used by e.g. Envivio 4Caster). **/
        private static const SYNCWORD_3 : uint = 0xFFF8;
        /** ADTS/ADIF sample rates index. **/
        private static const RATES : Array = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
        /** ADIF profile index (ADTS doesn't have Null). **/
        private static const PROFILES : Array = ['Null', 'Main', 'LC', 'SSR', 'LTP', 'SBR'];
        /** Byte data to be read **/
        private var _data : ByteArray;
        /* callback functions for audio selection, and parsing progress/complete */
        private var _callback_audioselect : Function;
        private var _callback_progress : Function;
        private var _callback_complete : Function;

        /** append new data */
        public function append(data : ByteArray) : void {
            _data.writeBytes(data);
        }

        /** cancel demux operation */
        public function cancel() : void {
            _data = null;
        }

        public function notifycomplete() : void {
            CONFIG::LOGGING {
            Log.debug("AAC: extracting AAC tags");
            }
            var audioTags : Vector.<FLVTag> = new Vector.<FLVTag>();
            /* parse AAC, convert Elementary Streams to TAG */
            _data.position = 0;
            var id3 : ID3 = new ID3(_data);
            // AAC should contain ID3 tag filled with a timestamp
            var frames : Vector.<AudioFrame> = AACDemuxer.getFrames(_data, _data.position);
            var adif : ByteArray = getADIF(_data, id3.len);
            var adifTag : FLVTag = new FLVTag(FLVTag.AAC_HEADER, id3.timestamp, id3.timestamp, true);
            adifTag.push(adif, 0, adif.length);
            audioTags.push(adifTag);

            var audioTag : FLVTag;
            var stamp : uint;
            var i : int = 0;

            while (i < frames.length) {
                stamp = Math.round(id3.timestamp + i * 1024 * 1000 / frames[i].rate);
                audioTag = new FLVTag(FLVTag.AAC_RAW, stamp, stamp, false);
                if (i != frames.length - 1) {
                    audioTag.push(_data, frames[i].start, frames[i].length);
                } else {
                    audioTag.push(_data, frames[i].start, _data.length - frames[i].start);
                }
                audioTags.push(audioTag);
                i++;
            }
            var audiotracks : Vector.<HLSAudioTrack> = new Vector.<HLSAudioTrack>();
            audiotracks.push(new HLSAudioTrack('AAC ES', HLSAudioTrack.FROM_DEMUX, 0, true));
            // report unique audio track. dont check return value as obviously the track will be selected
            _callback_audioselect(audiotracks);
            CONFIG::LOGGING {
            Log.debug("AAC: all tags extracted, callback demux");
            }
            _callback_progress(audioTags);
            _callback_complete();
        }

        public function AACDemuxer(callback_audioselect : Function, callback_progress : Function, callback_complete : Function) : void {
            _callback_audioselect = callback_audioselect;
            _callback_progress = callback_progress;
            _callback_complete = callback_complete;
            _data = new ByteArray();
        };

        public static function probe(data : ByteArray) : Boolean {
            var pos : uint = data.position;
            var id3 : ID3 = new ID3(data);
            // AAC should contain ID3 tag filled with a timestamp
            if (id3.hasTimestamp) {
                while (data.bytesAvailable > 1) {
                    // Check for ADTS header
                    var short : uint = data.readUnsignedShort();
                    if (short == SYNCWORD || short == SYNCWORD_2 || short == SYNCWORD_3) {
                        // rewind to sync word
                        data.position -= 2;
                        return true;
                    } else {
                        data.position--;
                    }
                }
                data.position = pos;
            }
            return false;
        }

        /** Get ADIF header from ADTS stream. **/
        public static function getADIF(adts : ByteArray, position : uint) : ByteArray {
            adts.position = position;
            var short : uint;
            // we need at least 6 bytes, 2 for sync word, 4 for frame length
            while ((adts.bytesAvailable > 5) && (short != SYNCWORD) && (short != SYNCWORD_2) && (short != SYNCWORD_3)) {
                short = adts.readUnsignedShort();
            }

            if (short == SYNCWORD || short == SYNCWORD_2 || short == SYNCWORD_3) {
                var profile : uint = (adts.readByte() & 0xF0) >> 6;
                // Correcting zero-index of ADIF and Flash playing only LC/HE.
                if (profile > 3) {
                    profile = 5;
                } else {
                    profile = 2;
                }
                adts.position--;
                var srate : uint = (adts.readByte() & 0x3C) >> 2;
                adts.position--;
                var channels : uint = (adts.readShort() & 0x01C0) >> 6;
            } else {
                throw new Error("Stream did not start with ADTS header.");
            }
            // 5 bits profile + 4 bits samplerate + 4 bits channels.
            var adif : ByteArray = new ByteArray();
            adif.writeByte((profile << 3) + (srate >> 1));
            adif.writeByte((srate << 7) + (channels << 3));
            CONFIG::LOGGING {
            Log.debug('AAC: ' + PROFILES[profile] + ', ' + RATES[srate] + ' Hz ' + channels + ' channel(s)');
            }
            // Reset position and return adif.
            adts.position -= 4;
            adif.position = 0;
            return adif;
        };

        /** Get a list with AAC frames from ADTS stream. **/
        public static function getFrames(adts : ByteArray, position : uint) : Vector.<AudioFrame> {
            var frames : Vector.<AudioFrame> = new Vector.<AudioFrame>();
            var frame_start : uint;
            var frame_length : uint;
            var id3 : ID3 = new ID3(adts);
            position += id3.len;
            // Get raw AAC frames from audio stream.
            adts.position = position;
            var samplerate : uint;
            // we need at least 6 bytes, 2 for sync word, 4 for frame length
            while (adts.bytesAvailable > 5) {
                // Check for ADTS header
                var short : uint = adts.readUnsignedShort();
                if (short == SYNCWORD || short == SYNCWORD_2 || short == SYNCWORD_3) {
                    // Store samplerate for offsetting timestamps.
                    if (!samplerate) {
                        samplerate = RATES[(adts.readByte() & 0x3C) >> 2];
                        adts.position--;
                    }
                    // Store raw AAC preceding this header.
                    if (frame_start) {
                        frames.push(new AudioFrame(frame_start, frame_length, frame_length, samplerate));
                    }
                    if (short == SYNCWORD_3) {
                        // ADTS header is 9 bytes.
                        frame_length = ((adts.readUnsignedInt() & 0x0003FFE0) >> 5) - 9;
                        frame_start = adts.position + 3;
                        adts.position += frame_length + 3;
                    } else {
                        // ADTS header is 7 bytes.
                        frame_length = ((adts.readUnsignedInt() & 0x0003FFE0) >> 5) - 7;
                        frame_start = adts.position + 1;
                        adts.position += frame_length + 1;
                    }
                } else {
                    CONFIG::LOGGING {
                    Log.debug("no ADTS header found, probing...");
                    }
                    adts.position--;
                }
            }
            if (frame_start) {
                // check if we have a complete frame available at the end, i.e. last found frame is fitting in this PES packet
                var overflow : int = frame_start + frame_length - adts.length;
                if (overflow <= 0 ) {
                    // no overflow, Write raw AAC after last header.
                    frames.push(new AudioFrame(frame_start, frame_length, frame_length, samplerate));
                } else {
                    CONFIG::LOGGING {
                    Log.debug2("ADTS overflow at the end of PES packet, missing " + overflow + " bytes to complete the ADTS frame");
                    }
                }
            }
            else if (frames.length == 0) {
                null;
                CONFIG::LOGGING {
                Log.warn("No ADTS headers found in this stream.");
                }
            }
            adts.position = position;
            return frames;
        };
    }
}

Fix for adif being found randomly in the stream

When searching from position=0 and reading 2 bytes at the time, FFF1 is
not found directly after the id3 when the id3 has an odd number of
bytes. Search should starry after id3 tag in order to be aligned
correctly.


////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2008-2013 Esri. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
////////////////////////////////////////////////////////////////////////////////
package com.esri.builder.supportClasses
{

import flash.filesystem.File;

public class FileUtil
{
    public static function generateUniqueRelativePath(baseDirectory:File, relativeFilePath:String, relativePathBlackList:Array = null):String
    {
        var extensionIndex:int = relativeFilePath.lastIndexOf(".");
        var extension:String = "";
        if (extensionIndex > -1)
        {
            extension = relativeFilePath.substr(extensionIndex);
            relativeFilePath = relativeFilePath.substr(0, extensionIndex);
        }
        var filenameTemplate:String = relativeFilePath + "_{0}" + extension;

        var uniqueWidgetConfigPath:String =
            LabelUtil.generateUniqueLabel(filenameTemplate,
                                          relativePathBlackList,
                                          function isNonExistentFile(uniqueFilePath:String, availableNames:Array):Boolean
                                          {
                                              if (availableNames
                                                  && availableNames.indexOf(uniqueFilePath) > -1)
                                              {
                                                  return false;
                                              }

                                              return !baseDirectory.resolvePath(uniqueFilePath).exists;
                                          });
        return uniqueWidgetConfigPath;
    }

    public static function getFileName(file:File):String
    {
        return file.name.replace("." + file.extension, "");
    }

    public static function findMatchingFiles(directory:File, pattern:RegExp):Array
    {
        var moduleFiles:Array = [];

        if (directory.isDirectory)
        {
            const files:Array = directory.getDirectoryListing();

            for each (var file:File in files)
            {
                if (file.isDirectory)
                {
                    moduleFiles = moduleFiles.concat(findMatchingFiles(file, pattern));
                }
                else if (pattern.test(file.name))
                {
                    moduleFiles.push(file);
                }
            }
        }

        return moduleFiles;
    }

    public static function findMatchingFile(directory:File, pattern:RegExp):File
    {
        var matchingFile:File;

        if (directory.isDirectory)
        {
            const files:Array = directory.getDirectoryListing();

            //sort files first to reduce unnecessary directory listing calls.
            files.sortOn("isDirectory");

            for each (var file:File in files)
            {
                if (file.isDirectory)
                {
                    matchingFile = findMatchingFile(file, pattern);
                }
                else if (pattern.test(file.name))
                {
                    matchingFile = file;
                }

                if (matchingFile)
                {
                    break;
                }
            }
        }

        return matchingFile;
    }
}
}

////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2008-2013 Esri. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
////////////////////////////////////////////////////////////////////////////////
package com.esri.builder.supportClasses
{

import flash.filesystem.File;

public class FileUtil
{
    public static function generateUniqueRelativePath(baseDirectory:File, relativeFilePath:String, relativePathBlackList:Array = null):String
    {
        var extensionIndex:int = relativeFilePath.lastIndexOf(".");
        var extension:String = "";
        if (extensionIndex > -1)
        {
            extension = relativeFilePath.substr(extensionIndex);
            relativeFilePath = relativeFilePath.substr(0, extensionIndex);
        }
        var filenameTemplate:String = relativeFilePath + "_{0}" + extension;

        var uniqueWidgetConfigPath:String =
            LabelUtil.generateUniqueLabel(filenameTemplate,
                                          relativePathBlackList,
                                          function isNonExistentFile(uniqueFilePath:String, availableNames:Array):Boolean
                                          {
                                              if (availableNames
                                                  && availableNames.indexOf(uniqueFilePath) > -1)
                                              {
                                                  return false;
                                              }

                                              return !baseDirectory.resolvePath(uniqueFilePath).exists;
                                          });
        return uniqueWidgetConfigPath;
    }

    public static function getFileName(file:File):String
    {
        return file.name.replace("." + file.extension, "");
    }

    public static function findMatchingFiles(directory:File, pattern:RegExp):Array
    {
        var moduleFiles:Array = [];

        if (directory.isDirectory)
        {
            const files:Array = directory.getDirectoryListing();

            for each (var file:File in files)
            {
                if (file.isDirectory)
                {
                    moduleFiles = moduleFiles.concat(findMatchingFiles(file, pattern));
                }
                else if (pattern.test(file.name))
                {
                    moduleFiles.push(file);
                }
            }
        }

        return moduleFiles;
    }

    public static function findMatchingFile(directory:File, pattern:RegExp):File
    {
        var matchingFile:File;

        if (directory.isDirectory)
        {
            const files:Array = directory.getDirectoryListing();

            //sort files first to reduce unnecessary directory listing calls.
            files.sortOn("isDirectory");

            for each (var file:File in files)
            {
                if (file.isDirectory)
                {
                    matchingFile = findMatchingFile(file, pattern);
                }
                else if (pattern.test(file.name))
                {
                    matchingFile = file;
                }

                if (matchingFile)
                {
                    break;
                }
            }
        }

        return matchingFile;
    }

    public static function ensureOSLineEndings(text:String):String
    {
        return text ? text.replace(/\n/g, File.lineEnding) : "";
    }
}
}

Add utility method to ensure OS line endings.


package org.mangui.chromeless {
    import flash.net.URLStream;

    import org.mangui.HLS.parsing.Level;
    import org.mangui.HLS.*;
    import org.mangui.HLS.utils.*;

    import flash.display.*;
    import flash.events.*;
    import flash.external.ExternalInterface;
    import flash.geom.Rectangle;
    import flash.media.Video;
    import flash.media.SoundTransform;
    import flash.media.StageVideo;
    import flash.media.StageVideoAvailability;
    import flash.utils.setTimeout;

    public class ChromelessPlayer extends Sprite {
        /** reference to the framework. **/
        private var _hls : HLS;
        /** Sheet to place on top of the video. **/
        private var _sheet : Sprite;
        /** Reference to the stage video element. **/
        private var _stageVideo : StageVideo = null;
        /** Reference to the video element. **/
        private var _video : Video = null;
        /** Video size **/
        private var _videoWidth : Number = 0;
        private var _videoHeight : Number = 0;
        /** current media position */
        private var _media_position : Number;
        private var _duration : Number;

        /** Initialization. **/
        public function ChromelessPlayer() : void {
            // Set stage properties
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.fullScreenSourceRect = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
            stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, _onStageVideoState);
            stage.addEventListener(Event.RESIZE, _onStageResize);
            // Draw sheet for catching clicks
            _sheet = new Sprite();
            _sheet.graphics.beginFill(0x000000, 0);
            _sheet.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
            _sheet.addEventListener(MouseEvent.CLICK, _clickHandler);
            _sheet.buttonMode = true;
            addChild(_sheet);
            // Connect getters to JS.
            ExternalInterface.addCallback("getLevel", _getLevel);
            ExternalInterface.addCallback("getLevels", _getLevels);
            ExternalInterface.addCallback("getMetrics", _getMetrics);
            ExternalInterface.addCallback("getDuration", _getDuration);
            ExternalInterface.addCallback("getPosition", _getPosition);
            ExternalInterface.addCallback("getState", _getState);
            ExternalInterface.addCallback("getType", _getType);
            ExternalInterface.addCallback("getmaxBufferLength", _getmaxBufferLength);
            ExternalInterface.addCallback("getminBufferLength", _getminBufferLength);
            ExternalInterface.addCallback("getbufferLength", _getbufferLength);
            ExternalInterface.addCallback("getLogDebug", _getLogDebug);
            ExternalInterface.addCallback("getLogDebug2", _getLogDebug2);
            ExternalInterface.addCallback("getflushLiveURLCache", _getflushLiveURLCache);
            ExternalInterface.addCallback("getstartFromLowestLevel", _getstartFromLowestLevel);
            ExternalInterface.addCallback("getseekFromLowestLevel", _getseekFromLowestLevel);
            ExternalInterface.addCallback("getJSURLStream", _getJSURLStream);
            ExternalInterface.addCallback("getPlayerVersion", _getPlayerVersion);
            ExternalInterface.addCallback("getAudioTrackList", _getAudioTrackList);
            ExternalInterface.addCallback("getAudioTrackId", _getAudioTrackId);
            // Connect calls to JS.
            ExternalInterface.addCallback("playerLoad", _load);
            ExternalInterface.addCallback("playerPlay", _play);
            ExternalInterface.addCallback("playerPause", _pause);
            ExternalInterface.addCallback("playerResume", _resume);
            ExternalInterface.addCallback("playerSeek", _seek);
            ExternalInterface.addCallback("playerStop", _stop);
            ExternalInterface.addCallback("playerVolume", _volume);
            ExternalInterface.addCallback("playerSetLevel", _setLevel);
            ExternalInterface.addCallback("playerSetmaxBufferLength", _setmaxBufferLength);
            ExternalInterface.addCallback("playerSetminBufferLength", _setminBufferLength);
            ExternalInterface.addCallback("playerSetflushLiveURLCache", _setflushLiveURLCache);
            ExternalInterface.addCallback("playerSetstartFromLowestLevel", _setstartFromLowestLevel);
            ExternalInterface.addCallback("playerSetseekFromLowestLevel", _setseekFromLowestLevel);
            ExternalInterface.addCallback("playerSetLogDebug", _setLogDebug);
            ExternalInterface.addCallback("playerSetLogDebug2", _setLogDebug2);
            ExternalInterface.addCallback("playerSetAudioTrack", _setAudioTrack);
            ExternalInterface.addCallback("playerSetJSURLStream", _setJSURLStream);

            setTimeout(_pingJavascript, 50);
        };

        /** Notify javascript the framework is ready. **/
        private function _pingJavascript() : void {
            ExternalInterface.call("onHLSReady", ExternalInterface.objectID);
        };

        /** Forward events from the framework. **/
        private function _completeHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onComplete");
            }
        };

        private function _errorHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onError", event.message);
            }
        };

        private function _fragmentHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onFragment", event.metrics.bandwidth, event.metrics.level, stage.stageWidth);
            }
        };

        private function _manifestHandler(event : HLSEvent) : void {
            _duration = event.levels[0].duration;
            if (ExternalInterface.available) {
                ExternalInterface.call("onManifest", event.levels[0].duration);
            }
        };

        private function _mediaTimeHandler(event : HLSEvent) : void {
            _duration = event.mediatime.duration;
            _media_position = event.mediatime.position;
            if (ExternalInterface.available) {
                ExternalInterface.call("onPosition", event.mediatime.position, event.mediatime.duration, event.mediatime.live_sliding);
            }

            var videoWidth : Number = _video ? _video.videoWidth : _stageVideo.videoWidth;
            var videoHeight : Number = _video ? _video.videoHeight : _stageVideo.videoHeight;

            if (videoWidth && videoHeight) {
                var changed : Boolean = _videoWidth != videoWidth || _videoHeight != videoHeight;
                if (changed) {
                    _videoHeight = videoHeight;
                    _videoWidth = videoWidth;
                    _resize();
                    if (ExternalInterface.available) {
                        ExternalInterface.call("onVideoSize", _videoWidth, _videoHeight);
                    }
                }
            }
        };

        private function _stateHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onState", event.state);
            }
        };

        private function _switchHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onSwitch", event.level);
            }
        };

        private function _audioTracksListChange(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onAudioTracksListChange", _getAudioTrackList());
            }
        }

        private function _audioTrackChange(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onAudioTrackChange", event.audioTrack);
            }
        }

        /** Javascript getters. **/
        private function _getLevel() : Number {
            return _hls.level;
        };

        private function _getLevels() : Vector.<Level> {
            return _hls.levels;
        };

        private function _getMetrics() : Object {
            return _hls.metrics;
        };

        private function _getDuration() : Number {
            return _duration;
        };

        private function _getPosition() : Number {
            return _hls.position;
        };

        private function _getState() : String {
            return _hls.state;
        };

        private function _getType() : String {
            return _hls.type;
        };

        private function _getbufferLength() : Number {
            return _hls.bufferLength;
        };

        private function _getmaxBufferLength() : Number {
            return _hls.maxBufferLength;
        };

        private function _getminBufferLength() : Number {
            return _hls.minBufferLength;
        };

        private function _getflushLiveURLCache() : Boolean {
            return _hls.flushLiveURLCache;
        };

        private function _getstartFromLowestLevel() : Boolean {
            return _hls.startFromLowestLevel;
        };

        private function _getseekFromLowestLevel() : Boolean {
            return _hls.seekFromLowestLevel;
        };

        private function _getLogDebug() : Boolean {
            return Log.LOG_DEBUG_ENABLED;
        };

        private function _getLogDebug2() : Boolean {
            return Log.LOG_DEBUG2_ENABLED;
        };

        private function _getJSURLStream() : Boolean {
            return (_hls.URLstream is JSURLStream);
        };

        private function _getPlayerVersion() : Number {
            return 2;
        };

        private function _getAudioTrackList() : Array {
            var list : Array = [];
            var vec : Vector.<HLSAudioTrack> = _hls.audioTracks;
            for (var i : Object in vec) {
                list.push(vec[i]);
            }
            return list;
        };

        private function _getAudioTrackId() : Number {
            return _hls.audioTrack;
        };

        /** Javascript calls. **/
        private function _load(url : String) : void {
            _hls.load(url);
        };

        private function _play() : void {
            _hls.stream.play();
        };

        private function _pause() : void {
            _hls.stream.pause();
        };

        private function _resume() : void {
            _hls.stream.resume();
        };

        private function _seek(position : Number) : void {
            _hls.stream.seek(position);
        };

        private function _stop() : void {
            _hls.stream.close();
        };

        private function _volume(percent : Number) : void {
            _hls.stream.soundTransform = new SoundTransform(percent / 100);
        };

        private function _setLevel(level : Number) : void {
            if (level != _hls.level) {
                _hls.level = level;
                if (!isNaN(_media_position) && level != -1) {
                    _hls.stream.seek(_media_position);
                }
            }
        };

        private function _setmaxBufferLength(new_len : Number) : void {
            _hls.maxBufferLength = new_len;
        };

        private function _setminBufferLength(new_len : Number) : void {
            _hls.minBufferLength = new_len;
        };

        private function _setflushLiveURLCache(flushLiveURLCache : Boolean) : void {
            _hls.flushLiveURLCache = flushLiveURLCache;
        };

        private function _setstartFromLowestLevel(startFromLowestLevel : Boolean) : void {
            _hls.startFromLowestLevel = startFromLowestLevel;
        };

        private function _setseekFromLowestLevel(seekFromLowestLevel : Boolean) : void {
            _hls.seekFromLowestLevel = seekFromLowestLevel;
        };

        private function _setLogDebug(debug : Boolean) : void {
            Log.LOG_DEBUG_ENABLED = debug;
        };

        private function _setLogDebug2(debug2 : Boolean) : void {
            Log.LOG_DEBUG2_ENABLED = debug2;
        };

        private function _setJSURLStream(jsURLstream : Boolean) : void {
            if (jsURLstream) {
                _hls.URLstream = JSURLStream as Class;
            } else {
                _hls.URLstream = URLStream as Class;
            }
        };

        private function _setAudioTrack(val : Number) : void {
            if (val == _hls.audioTrack) return;
            _hls.audioTrack = val;
            if (!isNaN(_media_position)) {
                _hls.stream.seek(_media_position);
            }
        };

        /** Mouse click handler. **/
        private function _clickHandler(event : MouseEvent) : void {
            if (stage.displayState == StageDisplayState.FULL_SCREEN_INTERACTIVE || stage.displayState == StageDisplayState.FULL_SCREEN) {
                stage.displayState = StageDisplayState.NORMAL;
            } else {
                stage.displayState = StageDisplayState.FULL_SCREEN;
            }
        };

        /** StageVideo detector. **/
        private function _onStageVideoState(event : StageVideoAvailabilityEvent) : void {
            var available : Boolean = (event.availability == StageVideoAvailability.AVAILABLE);
            _hls = new HLS();
            _hls.addEventListener(HLSEvent.PLAYBACK_COMPLETE, _completeHandler);
            _hls.addEventListener(HLSEvent.ERROR, _errorHandler);
            _hls.addEventListener(HLSEvent.FRAGMENT_LOADED, _fragmentHandler);
            _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestHandler);
            _hls.addEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler);
            _hls.addEventListener(HLSEvent.STATE, _stateHandler);
            _hls.addEventListener(HLSEvent.QUALITY_SWITCH, _switchHandler);
            _hls.addEventListener(HLSEvent.AUDIO_TRACKS_LIST_CHANGE, _audioTracksListChange);
            _hls.addEventListener(HLSEvent.AUDIO_TRACK_CHANGE, _audioTrackChange);

            if (available && stage.stageVideos.length > 0) {
                _stageVideo = stage.stageVideos[0];
                _stageVideo.viewPort = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
                _stageVideo.attachNetStream(_hls.stream);
            } else {
                _video = new Video(stage.stageWidth, stage.stageHeight);
                addChild(_video);
                _video.smoothing = true;
                _video.attachNetStream(_hls.stream);
            }
            stage.removeEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, _onStageVideoState);
        };

        private function _onStageResize(event : Event) : void {
            stage.fullScreenSourceRect = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
            _sheet.width = stage.stageWidth;
            _sheet.height = stage.stageHeight;
            _resize();
        };

        private function _resize() : void {
            var rect : Rectangle;
            rect = ScaleVideo.resizeRectangle(_videoWidth, _videoHeight, stage.stageWidth, stage.stageHeight);
            // resize video
            if (_video) {
                _video.width = rect.width;
                _video.height = rect.height;
                _video.x = rect.x;
                _video.y = rect.y;
            } else if (_stageVideo) {
                _stageVideo.viewPort = rect;
            }
        }
    }
}

package org.mangui.chromeless {
    import flash.net.URLStream;

    import org.mangui.HLS.parsing.Level;
    import org.mangui.HLS.*;
    import org.mangui.HLS.utils.*;

    import flash.display.*;
    import flash.events.*;
    import flash.external.ExternalInterface;
    import flash.geom.Rectangle;
    import flash.media.Video;
    import flash.media.SoundTransform;
    import flash.media.StageVideo;
    import flash.media.StageVideoAvailability;
    import flash.utils.setTimeout;

    public class ChromelessPlayer extends Sprite {
        /** reference to the framework. **/
        private var _hls : HLS;
        /** Sheet to place on top of the video. **/
        private var _sheet : Sprite;
        /** Reference to the stage video element. **/
        private var _stageVideo : StageVideo = null;
        /** Reference to the video element. **/
        private var _video : Video = null;
        /** Video size **/
        private var _videoWidth : Number = 0;
        private var _videoHeight : Number = 0;
        /** current media position */
        private var _media_position : Number;
        private var _duration : Number;

        /** Initialization. **/
        public function ChromelessPlayer() : void {
            // Set stage properties
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.fullScreenSourceRect = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
            stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, _onStageVideoState);
            stage.addEventListener(Event.RESIZE, _onStageResize);
            // Draw sheet for catching clicks
            _sheet = new Sprite();
            _sheet.graphics.beginFill(0x000000, 0);
            _sheet.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
            _sheet.addEventListener(MouseEvent.CLICK, _clickHandler);
            _sheet.buttonMode = true;
            addChild(_sheet);
            // Connect getters to JS.
            ExternalInterface.addCallback("getLevel", _getLevel);
            ExternalInterface.addCallback("getLevels", _getLevels);
            ExternalInterface.addCallback("getMetrics", _getMetrics);
            ExternalInterface.addCallback("getDuration", _getDuration);
            ExternalInterface.addCallback("getPosition", _getPosition);
            ExternalInterface.addCallback("getState", _getState);
            ExternalInterface.addCallback("getType", _getType);
            ExternalInterface.addCallback("getmaxBufferLength", _getmaxBufferLength);
            ExternalInterface.addCallback("getminBufferLength", _getminBufferLength);
            ExternalInterface.addCallback("getbufferLength", _getbufferLength);
            ExternalInterface.addCallback("getLogDebug", _getLogDebug);
            ExternalInterface.addCallback("getLogDebug2", _getLogDebug2);
            ExternalInterface.addCallback("getflushLiveURLCache", _getflushLiveURLCache);
            ExternalInterface.addCallback("getstartFromLowestLevel", _getstartFromLowestLevel);
            ExternalInterface.addCallback("getseekFromLowestLevel", _getseekFromLowestLevel);
            ExternalInterface.addCallback("getJSURLStream", _getJSURLStream);
            ExternalInterface.addCallback("getPlayerVersion", _getPlayerVersion);
            ExternalInterface.addCallback("getAudioTrackList", _getAudioTrackList);
            ExternalInterface.addCallback("getAudioTrackId", _getAudioTrackId);
            // Connect calls to JS.
            ExternalInterface.addCallback("playerLoad", _load);
            ExternalInterface.addCallback("playerPlay", _play);
            ExternalInterface.addCallback("playerPause", _pause);
            ExternalInterface.addCallback("playerResume", _resume);
            ExternalInterface.addCallback("playerSeek", _seek);
            ExternalInterface.addCallback("playerStop", _stop);
            ExternalInterface.addCallback("playerVolume", _volume);
            ExternalInterface.addCallback("playerSetLevel", _setLevel);
            ExternalInterface.addCallback("playerSmoothSetLevel", _smoothSetLevel);
            ExternalInterface.addCallback("playerSetmaxBufferLength", _setmaxBufferLength);
            ExternalInterface.addCallback("playerSetminBufferLength", _setminBufferLength);
            ExternalInterface.addCallback("playerSetflushLiveURLCache", _setflushLiveURLCache);
            ExternalInterface.addCallback("playerSetstartFromLowestLevel", _setstartFromLowestLevel);
            ExternalInterface.addCallback("playerSetseekFromLowestLevel", _setseekFromLowestLevel);
            ExternalInterface.addCallback("playerSetLogDebug", _setLogDebug);
            ExternalInterface.addCallback("playerSetLogDebug2", _setLogDebug2);
            ExternalInterface.addCallback("playerSetAudioTrack", _setAudioTrack);
            ExternalInterface.addCallback("playerSetJSURLStream", _setJSURLStream);

            setTimeout(_pingJavascript, 50);
        };

        /** Notify javascript the framework is ready. **/
        private function _pingJavascript() : void {
            ExternalInterface.call("onHLSReady", ExternalInterface.objectID);
        };

        /** Forward events from the framework. **/
        private function _completeHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onComplete");
            }
        };

        private function _errorHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onError", event.message);
            }
        };

        private function _fragmentHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onFragment", event.metrics.bandwidth, event.metrics.level, stage.stageWidth);
            }
        };

        private function _manifestHandler(event : HLSEvent) : void {
            _duration = event.levels[0].duration;
            if (ExternalInterface.available) {
                ExternalInterface.call("onManifest", event.levels[0].duration);
            }
        };

        private function _mediaTimeHandler(event : HLSEvent) : void {
            _duration = event.mediatime.duration;
            _media_position = event.mediatime.position;
            if (ExternalInterface.available) {
                ExternalInterface.call("onPosition", event.mediatime.position, event.mediatime.duration, event.mediatime.live_sliding);
            }

            var videoWidth : Number = _video ? _video.videoWidth : _stageVideo.videoWidth;
            var videoHeight : Number = _video ? _video.videoHeight : _stageVideo.videoHeight;

            if (videoWidth && videoHeight) {
                var changed : Boolean = _videoWidth != videoWidth || _videoHeight != videoHeight;
                if (changed) {
                    _videoHeight = videoHeight;
                    _videoWidth = videoWidth;
                    _resize();
                    if (ExternalInterface.available) {
                        ExternalInterface.call("onVideoSize", _videoWidth, _videoHeight);
                    }
                }
            }
        };

        private function _stateHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onState", event.state);
            }
        };

        private function _switchHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onSwitch", event.level);
            }
        };

        private function _audioTracksListChange(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onAudioTracksListChange", _getAudioTrackList());
            }
        }

        private function _audioTrackChange(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onAudioTrackChange", event.audioTrack);
            }
        }

        /** Javascript getters. **/
        private function _getLevel() : Number {
            return _hls.level;
        };

        private function _getLevels() : Vector.<Level> {
            return _hls.levels;
        };

        private function _getMetrics() : Object {
            return _hls.metrics;
        };

        private function _getDuration() : Number {
            return _duration;
        };

        private function _getPosition() : Number {
            return _hls.position;
        };

        private function _getState() : String {
            return _hls.state;
        };

        private function _getType() : String {
            return _hls.type;
        };

        private function _getbufferLength() : Number {
            return _hls.bufferLength;
        };

        private function _getmaxBufferLength() : Number {
            return _hls.maxBufferLength;
        };

        private function _getminBufferLength() : Number {
            return _hls.minBufferLength;
        };

        private function _getflushLiveURLCache() : Boolean {
            return _hls.flushLiveURLCache;
        };

        private function _getstartFromLowestLevel() : Boolean {
            return _hls.startFromLowestLevel;
        };

        private function _getseekFromLowestLevel() : Boolean {
            return _hls.seekFromLowestLevel;
        };

        private function _getLogDebug() : Boolean {
            return Log.LOG_DEBUG_ENABLED;
        };

        private function _getLogDebug2() : Boolean {
            return Log.LOG_DEBUG2_ENABLED;
        };

        private function _getJSURLStream() : Boolean {
            return (_hls.URLstream is JSURLStream);
        };

        private function _getPlayerVersion() : Number {
            return 2;
        };

        private function _getAudioTrackList() : Array {
            var list : Array = [];
            var vec : Vector.<HLSAudioTrack> = _hls.audioTracks;
            for (var i : Object in vec) {
                list.push(vec[i]);
            }
            return list;
        };

        private function _getAudioTrackId() : Number {
            return _hls.audioTrack;
        };

        /** Javascript calls. **/
        private function _load(url : String) : void {
            _hls.load(url);
        };

        private function _play() : void {
            _hls.stream.play();
        };

        private function _pause() : void {
            _hls.stream.pause();
        };

        private function _resume() : void {
            _hls.stream.resume();
        };

        private function _seek(position : Number) : void {
            _hls.stream.seek(position);
        };

        private function _stop() : void {
            _hls.stream.close();
        };

        private function _volume(percent : Number) : void {
            _hls.stream.soundTransform = new SoundTransform(percent / 100);
        };

        private function _setLevel(level : Number) : void {
            _smoothSetLevel(level);
            if (!isNaN(_media_position) && level != -1) {
                _hls.stream.seek(_media_position);
            }
        };

        private function _smoothSetLevel(level: Number) : void {
            if (level != _hls.level) {
              _hls.level = level;
            }
        };

        private function _setmaxBufferLength(new_len : Number) : void {
            _hls.maxBufferLength = new_len;
        };

        private function _setminBufferLength(new_len : Number) : void {
            _hls.minBufferLength = new_len;
        };

        private function _setflushLiveURLCache(flushLiveURLCache : Boolean) : void {
            _hls.flushLiveURLCache = flushLiveURLCache;
        };

        private function _setstartFromLowestLevel(startFromLowestLevel : Boolean) : void {
            _hls.startFromLowestLevel = startFromLowestLevel;
        };

        private function _setseekFromLowestLevel(seekFromLowestLevel : Boolean) : void {
            _hls.seekFromLowestLevel = seekFromLowestLevel;
        };

        private function _setLogDebug(debug : Boolean) : void {
            Log.LOG_DEBUG_ENABLED = debug;
        };

        private function _setLogDebug2(debug2 : Boolean) : void {
            Log.LOG_DEBUG2_ENABLED = debug2;
        };

        private function _setJSURLStream(jsURLstream : Boolean) : void {
            if (jsURLstream) {
                _hls.URLstream = JSURLStream as Class;
            } else {
                _hls.URLstream = URLStream as Class;
            }
        };

        private function _setAudioTrack(val : Number) : void {
            if (val == _hls.audioTrack) return;
            _hls.audioTrack = val;
            if (!isNaN(_media_position)) {
                _hls.stream.seek(_media_position);
            }
        };

        /** Mouse click handler. **/
        private function _clickHandler(event : MouseEvent) : void {
            if (stage.displayState == StageDisplayState.FULL_SCREEN_INTERACTIVE || stage.displayState == StageDisplayState.FULL_SCREEN) {
                stage.displayState = StageDisplayState.NORMAL;
            } else {
                stage.displayState = StageDisplayState.FULL_SCREEN;
            }
        };

        /** StageVideo detector. **/
        private function _onStageVideoState(event : StageVideoAvailabilityEvent) : void {
            var available : Boolean = (event.availability == StageVideoAvailability.AVAILABLE);
            _hls = new HLS();
            _hls.addEventListener(HLSEvent.PLAYBACK_COMPLETE, _completeHandler);
            _hls.addEventListener(HLSEvent.ERROR, _errorHandler);
            _hls.addEventListener(HLSEvent.FRAGMENT_LOADED, _fragmentHandler);
            _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestHandler);
            _hls.addEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler);
            _hls.addEventListener(HLSEvent.STATE, _stateHandler);
            _hls.addEventListener(HLSEvent.QUALITY_SWITCH, _switchHandler);
            _hls.addEventListener(HLSEvent.AUDIO_TRACKS_LIST_CHANGE, _audioTracksListChange);
            _hls.addEventListener(HLSEvent.AUDIO_TRACK_CHANGE, _audioTrackChange);

            if (available && stage.stageVideos.length > 0) {
                _stageVideo = stage.stageVideos[0];
                _stageVideo.viewPort = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
                _stageVideo.attachNetStream(_hls.stream);
            } else {
                _video = new Video(stage.stageWidth, stage.stageHeight);
                addChild(_video);
                _video.smoothing = true;
                _video.attachNetStream(_hls.stream);
            }
            stage.removeEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, _onStageVideoState);
        };

        private function _onStageResize(event : Event) : void {
            stage.fullScreenSourceRect = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
            _sheet.width = stage.stageWidth;
            _sheet.height = stage.stageHeight;
            _resize();
        };

        private function _resize() : void {
            var rect : Rectangle;
            rect = ScaleVideo.resizeRectangle(_videoWidth, _videoHeight, stage.stageWidth, stage.stageHeight);
            // resize video
            if (_video) {
                _video.width = rect.width;
                _video.height = rect.height;
                _video.x = rect.x;
                _video.y = rect.y;
            } else if (_stageVideo) {
                _stageVideo.viewPort = rect;
            }
        }
    }
}

create playerSmoothSetLevel on ChromelessPlayer to change level without seeking


package aerys.minko.render.material.realistic
{
	import aerys.minko.render.Effect;
	import aerys.minko.render.material.environment.EnvironmentMappingProperties;
	import aerys.minko.render.material.phong.PhongEffect;
	import aerys.minko.render.material.phong.PhongMaterial;
	import aerys.minko.render.resource.texture.ITextureResource;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.binding.IDataProvider;

	import flash.utils.Dictionary;

	public class RealisticMaterial extends PhongMaterial
	{
		private static const DEFAULT_NAME		: String		= 'RealisticMaterial';
		private static const SCENE_TO_EFFECT	: Dictionary	= new Dictionary(true);

		public function get environmentMap() : ITextureResource
		{
			return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP) as ITextureResource;
		}
		public function set environmentMap(value : ITextureResource) : void
		{
			setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP, value);
		}

        public function get environmentMapFiltering() : uint
        {
            return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_FILTERING);
        }
        public function set environmentMapFiltering(value : uint) : void
        {
            setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_FILTERING, value);
        }

        public function get environmentMapMipMapping() : uint
        {
            return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_MIPMAPPING);
        }
        public function set environmentMapMipMapping(value : uint) : void
        {
            setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_MIPMAPPING, value);
        }

        public function get environmentMapWrapping() : uint
        {
            return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_WRAPPING);
        }
        public function set environmentMapWrapping(value : uint) : void
        {
            setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_WRAPPING, value);
        }

        public function get environmentMapFormat() : uint
        {
            return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_FORMAT);
        }
        public function set environmentMapFormat(value : uint) : void
        {
            setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_FORMAT, value);
        }

		public function get reflectivity() : Number
		{
			return getProperty(EnvironmentMappingProperties.REFLECTIVITY) as Number;
		}
		public function set reflectivity(value : Number) : void
		{
			setProperty(EnvironmentMappingProperties.REFLECTIVITY, value);
		}

		public function get environmentMappingType() : uint
		{
			return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAPPING_TYPE) as uint;
		}
		public function set environmentMappingType(value : uint) : void
		{
			setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAPPING_TYPE, value);
		}

		public function get environmentBlending() : uint
		{
			return getProperty(EnvironmentMappingProperties.ENVIRONMENT_BLENDING) as uint;
		}
		public function set environmentBlending(value : uint) : void
		{
			setProperty(EnvironmentMappingProperties.ENVIRONMENT_BLENDING, value);
		}

		public function RealisticMaterial(properties	: Object	= null,
										  effect		: Effect	= null,
										  name			: String	= DEFAULT_NAME)
		{
			super(properties, effect || new PhongEffect(new RealisticShader()), name);
		}

        override public function clone() : IDataProvider
        {
            return new RealisticMaterial(this, effect, name);
        }
	}
}

package aerys.minko.render.material.realistic
{
	import aerys.minko.render.Effect;
	import aerys.minko.render.material.environment.EnvironmentMappingProperties;
	import aerys.minko.render.material.phong.PhongEffect;
	import aerys.minko.render.material.phong.PhongMaterial;
	import aerys.minko.render.resource.texture.ITextureResource;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.binding.IDataProvider;

	import flash.utils.Dictionary;

	public class RealisticMaterial extends PhongMaterial
	{
		private static const DEFAULT_NAME		: String		= 'RealisticMaterial';
        private static const DEFAULT_EFFECT     : Effect        = new PhongEffect(
            new RealisticShader()
        );

		public function get environmentMap() : ITextureResource
		{
			return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP) as ITextureResource;
		}
		public function set environmentMap(value : ITextureResource) : void
		{
			setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP, value);
		}

        public function get environmentMapFiltering() : uint
        {
            return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_FILTERING);
        }
        public function set environmentMapFiltering(value : uint) : void
        {
            setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_FILTERING, value);
        }

        public function get environmentMapMipMapping() : uint
        {
            return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_MIPMAPPING);
        }
        public function set environmentMapMipMapping(value : uint) : void
        {
            setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_MIPMAPPING, value);
        }

        public function get environmentMapWrapping() : uint
        {
            return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_WRAPPING);
        }
        public function set environmentMapWrapping(value : uint) : void
        {
            setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_WRAPPING, value);
        }

        public function get environmentMapFormat() : uint
        {
            return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_FORMAT);
        }
        public function set environmentMapFormat(value : uint) : void
        {
            setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP_FORMAT, value);
        }

		public function get reflectivity() : Number
		{
			return getProperty(EnvironmentMappingProperties.REFLECTIVITY) as Number;
		}
		public function set reflectivity(value : Number) : void
		{
			setProperty(EnvironmentMappingProperties.REFLECTIVITY, value);
		}

		public function get environmentMappingType() : uint
		{
			return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAPPING_TYPE) as uint;
		}
		public function set environmentMappingType(value : uint) : void
		{
			setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAPPING_TYPE, value);
		}

		public function get environmentBlending() : uint
		{
			return getProperty(EnvironmentMappingProperties.ENVIRONMENT_BLENDING) as uint;
		}
		public function set environmentBlending(value : uint) : void
		{
			setProperty(EnvironmentMappingProperties.ENVIRONMENT_BLENDING, value);
		}

		public function RealisticMaterial(properties	: Object	= null,
										  effect		: Effect	= null,
										  name			: String	= DEFAULT_NAME)
		{
			super(properties, effect || DEFAULT_EFFECT, name);
		}

        override public function clone() : IDataProvider
        {
            return new RealisticMaterial(this, effect, name);
        }
	}
}

define a static const default effect for the RealisticMaterial and use it properly in the constructor


//
// Flump - Copyright 2013 Flump Authors

package flump.display {

import flump.mold.KeyframeMold;
import flump.mold.LayerMold;

import starling.animation.IAnimatable;
import starling.display.DisplayObject;
import starling.display.Sprite;

/**
 * A logical wrapper around the DisplayObject(s) residing on the timeline of a single layer of a
 * Movie. Responsible for efficiently managing the creation and display of the DisplayObjects for
 * this layer on each frame.
 */
internal class Layer
    implements IAnimatable
{
    public function Layer (movie :Movie, src :LayerMold, library :Library, flipbook :Boolean) {
        _keyframes = src.keyframes;
        _movie = movie;
        _name = src.name;

        const lastKf :KeyframeMold = _keyframes[_keyframes.length - 1];
        _numFrames = lastKf.index + lastKf.duration;

        var lastItem :String;
        for (var ii :int = 0; ii < _keyframes.length && lastItem == null; ii++) {
            lastItem = _keyframes[ii].ref;
        }
        if (!flipbook && lastItem == null) {
            // The layer is empty.
            _currentDisplay = new Sprite();
            _movie.addChild(_currentDisplay);
        } else {
            // Create the display objects for each keyframe.
            // If multiple consecutive keyframes refer to the same library item,
            // we reuse that item across those frames.
            _displays = new Vector.<DisplayObject>(_keyframes.length, true);
            for (ii = 0; ii < _keyframes.length; ++ii) {
                var kf :KeyframeMold = _keyframes[ii];
                var display :DisplayObject = null;
                if (ii > 0 && _keyframes[ii - 1].ref == kf.ref) {
                    display = _displays[ii - 1];
                } else if (kf.ref == null) {
                    display = new Sprite();
                } else {
                    display = library.createDisplayObject(kf.ref);
                }
                _displays[ii] = display;
                display.visible = false;
                _movie.addChild(display);
            }
            _currentDisplay = _displays[0];
            _currentDisplay.visible = true;

            _frameOvershootDisplay = new Sprite();
            _frameOvershootDisplay.visible = false;
            _movie.addChild(_frameOvershootDisplay);
        }

        _currentDisplay.name = _name;
    }

    /** Called by Movie when we loop. */
    public function movieLooped () :void {
        _needsKeyframeUpdate = true;
        _keyframeIdx = 0;
    }

    /** Advances the playhead by the give number of seconds. From IAnimatable. */
    public function advanceTime (dt :Number) :void {
        if (_currentDisplay is IAnimatable) {
            IAnimatable(_currentDisplay).advanceTime(dt);
        }
    }

    public function drawFrame (frame :int) :void {
        if (_displays == null) {
            // We have nothing to display.
            return;

        } else if (frame >= _numFrames) {
            // We've overshot our final frame. Show an empty sprite.
            if (_currentDisplay != _frameOvershootDisplay) {
                _currentDisplay.name = null;
                _currentDisplay.visible = false;
                _currentDisplay = _frameOvershootDisplay;
                _currentDisplay.name = _name;
            }
            // keep our keyframeIdx updated
            _keyframeIdx = _keyframes.length - 1;
            _needsKeyframeUpdate = true;
            return;
        }

        while (_keyframeIdx < _keyframes.length - 1 && _keyframes[_keyframeIdx + 1].index <= frame) {
            _keyframeIdx++;
            _needsKeyframeUpdate = true;
        }

        if (_needsKeyframeUpdate) {
            // Swap in the proper DisplayObject for this keyframe.
            const disp :DisplayObject = _displays[_keyframeIdx];
            if (_currentDisplay != disp) {
                _currentDisplay.name = null;
                _currentDisplay.visible = false;
                // If we're swapping in a Movie, reset its timeline.
                if (disp is Movie) {
                    Movie(disp).addedToLayer();
                }
                _currentDisplay = disp;
                _currentDisplay.name = _name;
            }
        }
        _needsKeyframeUpdate = false;

        const kf :KeyframeMold = _keyframes[_keyframeIdx];
        const layer :DisplayObject = _currentDisplay;
        if (_keyframeIdx == _keyframes.length - 1 || kf.index == frame || !kf.tweened) {
            layer.x = kf.x;
            layer.y = kf.y;
            layer.scaleX = kf.scaleX;
            layer.scaleY = kf.scaleY;
            layer.skewX = kf.skewX;
            layer.skewY = kf.skewY;
            layer.alpha = kf.alpha;
        } else {
            var interped :Number = (frame - kf.index) / kf.duration;
            var ease :Number = kf.ease;
            if (ease != 0) {
                var t :Number;
                if (ease < 0) {
                    // Ease in
                    var inv :Number = 1 - interped;
                    t = 1 - inv * inv;
                    ease = -ease;
                } else {
                    // Ease out
                    t = interped * interped;
                }
                interped = ease * t + (1 - ease) * interped;
            }
            const nextKf :KeyframeMold = _keyframes[_keyframeIdx + 1];
            layer.x = kf.x + (nextKf.x - kf.x) * interped;
            layer.y = kf.y + (nextKf.y - kf.y) * interped;
            layer.scaleX = kf.scaleX + (nextKf.scaleX - kf.scaleX) * interped;
            layer.scaleY = kf.scaleY + (nextKf.scaleY - kf.scaleY) * interped;
            layer.skewX = kf.skewX + (nextKf.skewX - kf.skewX) * interped;
            layer.skewY = kf.skewY + (nextKf.skewY - kf.skewY) * interped;
            layer.alpha = kf.alpha + (nextKf.alpha - kf.alpha) * interped;
        }
        layer.pivotX = kf.pivotX;
        layer.pivotY = kf.pivotY;
        layer.visible = kf.visible;
    }

    protected var _keyframes :Vector.<KeyframeMold>;
    protected var _numFrames :int;
    // Stores this layer's DisplayObjects indexed by keyframe.
    protected var _displays :Vector.<DisplayObject>;
    // Created if the layer has fewer frames than its parent movie. If the layer is told to
    // draw a frame past its last frame, it will display this empty sprite.
    protected var _frameOvershootDisplay :Sprite;
    // The current DisplayObject being rendered for this layer
    protected var _currentDisplay :DisplayObject;
    protected var _movie :Movie; // The movie this layer belongs to
    // The index of the last keyframe drawn in drawFrame. Updated in drawFrame. When the parent
    // movie loops, it resets all of its layers' keyframeIdx's to 0.
    protected var _keyframeIdx :int;
    // true if the keyframe has changed since the last drawFrame
    protected var _needsKeyframeUpdate :Boolean;
    // name of the layer
    protected var _name :String;
}
}

//
// Flump - Copyright 2013 Flump Authors

package flump.display {

import flump.mold.KeyframeMold;
import flump.mold.LayerMold;

import starling.animation.IAnimatable;
import starling.display.DisplayObject;
import starling.display.Sprite;

/**
 * A logical wrapper around the DisplayObject(s) residing on the timeline of a single layer of a
 * Movie. Responsible for efficiently managing the creation and display of the DisplayObjects for
 * this layer on each frame.
 */
internal class Layer
    implements IAnimatable
{
    public function Layer (movie :Movie, src :LayerMold, library :Library, flipbook :Boolean) {
        _keyframes = src.keyframes;
        _movie = movie;
        _name = src.name;

        const lastKf :KeyframeMold = _keyframes[_keyframes.length - 1];
        _numFrames = lastKf.index + lastKf.duration;

        var lastItem :String;
        for (var ii :int = 0; ii < _keyframes.length && lastItem == null; ii++) {
            lastItem = _keyframes[ii].ref;
        }
        if (!flipbook && lastItem == null) {
            // The layer is empty.
            _currentDisplay = new Sprite();
            _movie.addChild(_currentDisplay);
        } else {
            // Create the display objects for each keyframe.
            // If multiple consecutive keyframes refer to the same library item,
            // we reuse that item across those frames.
            _displays = new Vector.<DisplayObject>(_keyframes.length, true);
            for (ii = 0; ii < _keyframes.length; ++ii) {
                var kf :KeyframeMold = _keyframes[ii];
                var display :DisplayObject = null;
                if (ii > 0 && _keyframes[ii - 1].ref == kf.ref) {
                    display = _displays[ii - 1];
                } else if (kf.ref == null) {
                    display = new Sprite();
                } else {
                    display = library.createDisplayObject(kf.ref);
                }
                _displays[ii] = display;
                display.visible = false;
                _movie.addChild(display);
            }
            _currentDisplay = _displays[0];
            _currentDisplay.visible = true;
        }

        _currentDisplay.name = _name;
    }

    /** Called by Movie when we loop. */
    public function movieLooped () :void {
        _needsKeyframeUpdate = true;
        _keyframeIdx = 0;
    }

    /** Advances the playhead by the give number of seconds. From IAnimatable. */
    public function advanceTime (dt :Number) :void {
        if (_currentDisplay is IAnimatable) {
            IAnimatable(_currentDisplay).advanceTime(dt);
        }
    }

    public function drawFrame (frame :int) :void {
        if (_displays == null) {
            // We have nothing to display.
            return;

        } else if (frame >= _numFrames) {
            // We've overshot our final frame. Hide the display
            _currentDisplay.visible = false;
            // keep our keyframeIdx updated
            _keyframeIdx = _keyframes.length - 1;
            _needsKeyframeUpdate = true;
            return;
        }

        while (_keyframeIdx < _keyframes.length - 1 && _keyframes[_keyframeIdx + 1].index <= frame) {
            _keyframeIdx++;
            _needsKeyframeUpdate = true;
        }

        if (_needsKeyframeUpdate) {
            // Swap in the proper DisplayObject for this keyframe.
            const disp :DisplayObject = _displays[_keyframeIdx];
            if (_currentDisplay != disp) {
                _currentDisplay.name = null;
                _currentDisplay.visible = false;
                // If we're swapping in a Movie, reset its timeline.
                if (disp is Movie) {
                    Movie(disp).addedToLayer();
                }
                _currentDisplay = disp;
                _currentDisplay.name = _name;
            }
        }
        _needsKeyframeUpdate = false;

        const kf :KeyframeMold = _keyframes[_keyframeIdx];
        const layer :DisplayObject = _currentDisplay;
        if (_keyframeIdx == _keyframes.length - 1 || kf.index == frame || !kf.tweened) {
            layer.x = kf.x;
            layer.y = kf.y;
            layer.scaleX = kf.scaleX;
            layer.scaleY = kf.scaleY;
            layer.skewX = kf.skewX;
            layer.skewY = kf.skewY;
            layer.alpha = kf.alpha;
        } else {
            var interped :Number = (frame - kf.index) / kf.duration;
            var ease :Number = kf.ease;
            if (ease != 0) {
                var t :Number;
                if (ease < 0) {
                    // Ease in
                    var inv :Number = 1 - interped;
                    t = 1 - inv * inv;
                    ease = -ease;
                } else {
                    // Ease out
                    t = interped * interped;
                }
                interped = ease * t + (1 - ease) * interped;
            }
            const nextKf :KeyframeMold = _keyframes[_keyframeIdx + 1];
            layer.x = kf.x + (nextKf.x - kf.x) * interped;
            layer.y = kf.y + (nextKf.y - kf.y) * interped;
            layer.scaleX = kf.scaleX + (nextKf.scaleX - kf.scaleX) * interped;
            layer.scaleY = kf.scaleY + (nextKf.scaleY - kf.scaleY) * interped;
            layer.skewX = kf.skewX + (nextKf.skewX - kf.skewX) * interped;
            layer.skewY = kf.skewY + (nextKf.skewY - kf.skewY) * interped;
            layer.alpha = kf.alpha + (nextKf.alpha - kf.alpha) * interped;
        }
        layer.pivotX = kf.pivotX;
        layer.pivotY = kf.pivotY;
        layer.visible = kf.visible;
    }

    protected var _keyframes :Vector.<KeyframeMold>;
    protected var _numFrames :int;
    // Stores this layer's DisplayObjects indexed by keyframe.
    protected var _displays :Vector.<DisplayObject>;
    // The current DisplayObject being rendered for this layer
    protected var _currentDisplay :DisplayObject;
    protected var _movie :Movie; // The movie this layer belongs to
    // The index of the last keyframe drawn in drawFrame. Updated in drawFrame. When the parent
    // movie loops, it resets all of its layers' keyframeIdx's to 0.
    protected var _keyframeIdx :int;
    // true if the keyframe has changed since the last drawFrame
    protected var _needsKeyframeUpdate :Boolean;
    // name of the layer
    protected var _name :String;
}
}

remove frameOverShootDisplay sprite

remove frameOverShootDisplay sprite


//
// $Id$

package com.threerings.flash {

import flash.display.Sprite;

import flash.events.Event;

/**
 * Convenience superclass to use for sprites that need to update every frame.
 * (One must be very careful to remove all ENTER_FRAME listeners when not needed, as they
 * will prevent an object from being garbage collected!)
 */
public class FrameSprite extends Sprite
{
    public function FrameSprite ()
    {
        addEventListener(Event.ADDED_TO_STAGE, handleAdded);
        addEventListener(Event.REMOVED_FROM_STAGE, handleRemoved);
    }

    /**
     * Called when we're added to the stage.
     */
    protected function handleAdded (... ignored) :void
    {
        addEventListener(Event.ENTER_FRAME, handleFrame);
        handleFrame(); // update immediately
    }

    /**
     * Called when we're added to the stage.
     */
    protected function handleRemoved (... ignored) :void
    {
        removeEventListener(Event.ENTER_FRAME, handleFrame);
    }

    /**
     * Called to update our visual appearance prior to each frame.
     */
    protected function handleFrame (... ignored) :void
    {
        // nothing here. Override in yor subclass.
    }
}

}

//
// $Id$

package com.threerings.flash {

import flash.display.Sprite;

import flash.events.Event;

/**
 * Convenience superclass to use for sprites that need to update every frame.
 * (One must be very careful to remove all ENTER_FRAME listeners when not needed, as they
 * will prevent an object from being garbage collected!)
 */
public class FrameSprite extends Sprite
{
    /**
     * @param renderFrameUponAdding if true, the handleFrame() method
     *        is called whenever an ADDED_TO_STAGE event is received.
     */
    public function FrameSprite (renderFrameUponAdding :Boolean = true)
    {
        _renderOnAdd = renderFrameUponAdding;

        addEventListener(Event.ADDED_TO_STAGE, handleAdded);
        addEventListener(Event.REMOVED_FROM_STAGE, handleRemoved);
    }

    /**
     * Called when we're added to the stage.
     */
    protected function handleAdded (... ignored) :void
    {
        addEventListener(Event.ENTER_FRAME, handleFrame);
        if (_renderOnAdd) {
            handleFrame(); // update immediately
        }
    }

    /**
     * Called when we're added to the stage.
     */
    protected function handleRemoved (... ignored) :void
    {
        removeEventListener(Event.ENTER_FRAME, handleFrame);
    }

    /**
     * Called to update our visual appearance prior to each frame.
     */
    protected function handleFrame (... ignored) :void
    {
        // nothing here. Override in yor subclass.
    }

    /** Should we call handleFrame() when we get ADDED_TO_STAGE? */
    protected var _renderOnAdd :Boolean;
}

}

Allow subclasses to choose not to call handleFrame() when
ADDED_TO_STAGE is received.

git-svn-id: b675b909355d5cf946977f44a8ec5a6ceb3782e4@253 ed5b42cb-e716-0410-a449-f6a68f950b19