package
{
	import com.adobe.images.PNGEncoder;

	import flash.desktop.NativeApplication;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.InvokeEvent;
	import flash.events.TimerEvent;
	import flash.filesystem.File;
	import flash.filesystem.FileStream;
	import flash.net.URLRequest;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.utils.ByteArray;
	import flash.utils.Timer;

	public class swf2png extends Sprite
	{
		public var outputWidth:int;
		public var outputHeight:int;
		public var loadedSwf:MovieClip;
		public var loader:Loader;
		private var counter:int = 0;
		private var timer:Timer;
		private var totalFrames:int;
		private var inputFileName:String;
		private var inputFilePath:String;
		private var prefix:String;
		private var outfield:TextField;
		private var outputDirPath:String;

		public function swf2png() {
			NativeApplication.nativeApplication.addEventListener(InvokeEvent.INVOKE, onInvoke);
			outfield = new TextField();
			outfield.autoSize = TextFieldAutoSize.LEFT;
			stage.addChild(outfield);
			stage.frameRate = 12;
		}

		//Loads in file
		private function loadSwf():void {

			loader = new Loader();
			outfield.appendText("\nLoading " + inputFilePath);
			loader.load(new URLRequest("file://" + inputFilePath));
			loader.contentLoaderInfo.addEventListener(Event.INIT, startLoop);

		}

		//Event handler called when the swf is loaded. Sets it up and starts the export loop
		private function startLoop(ev:Event):void {
			loadedSwf = MovieClip(ev.target.content);
			outputWidth = Math.ceil(ev.target.width);
			outputHeight = Math.ceil(ev.target.height);
			outfield.appendText("\nLoaded!");
			stopClip(loadedSwf);
			goToFrame(loadedSwf, 0);
			totalFrames = loadedSwf.totalFrames;
			timer = new Timer(1);

			timer.addEventListener(TimerEvent.TIMER, step);
			timer.start();

		}

		//Called for every frame
		private function step(ev:TimerEvent):void {
			goToFrame(loadedSwf, counter);
			saveFrame();
			counter++;
			if(counter > totalFrames) {
				timer.stop();
				exit(0);
				return;
			}
		}

		//Saves the current frame of the loader object to a png
		private function saveFrame():void {
			var bitmapData:BitmapData = new BitmapData(outputWidth, outputHeight, true, 0x0);
			bitmapData.draw(loader);
			var bytearr:ByteArray = PNGEncoder.encode(bitmapData);
			var outfileName:String = outputDirPath + File.separator + prefix + counter + ".png"
			var file:File = new File(outfileName);
			outfield.appendText("\nPrefix: " + prefix);
			outfield.appendText("\nWriting: " + outfileName);
			var stream:FileStream = new FileStream();
			stream.open(file, "write");
			stream.writeBytes(bytearr);
			stream.close();
		}

		//Stops the movie clip and all its subclips.
		private function stopClip(inMc:MovieClip):void {
			var l:int = inMc.numChildren;
			for (var i:int = 0; i < l; i++)
			{
				var mc:MovieClip = inMc.getChildAt(i) as MovieClip;
				if(mc) {
					mc.stop();
					if(mc.numChildren > 0) {
						stopClip(mc);
					}
				}
			}
			inMc.stop();
		}

		//Traverses the movie clip and sets the current frame for all subclips too, looping them where needed.
		private function goToFrame(inMc:MovieClip, frameNo:int):void {
			var l:int = inMc.numChildren;
			for (var i:int = 0; i < l; i++)
			{
				var mc:MovieClip = inMc.getChildAt(i) as MovieClip;
				if(mc) {
					mc.gotoAndStop(frameNo % inMc.totalFrames);
					if(mc.numChildren > 0) {
						goToFrame(mc, frameNo);
					}
				}
			}
			inMc.gotoAndStop(frameNo % inMc.totalFrames);
		}

		//Finds and checks for existance of input file
		private function getInputFile(ev:InvokeEvent):String {
			if(ev.arguments && ev.arguments.length) {
				inputFileName = ev.arguments[0];
				var matches:Array = inputFileName.match(/([a-zA-Z0-9_\-\+\.]*?)\.swf$/);
				if(!matches) {
					// File inputFileName not valid
					exit(2);
					return "";
				}
				prefix = matches[1];
				var f:File = new File(ev.currentDirectory.nativePath);
				f = f.resolvePath(inputFileName);
				if(!f.exists) {
					outfield.appendText("\nInput file not found!");
					//Input file not found
					exit(3);
					return "";
				}
				return f.nativePath;
			}
			else {
				//App opened without input data
				exit(1);
				return "";
			}
		}

		//Finds and checks for existance of output directory
		private function getOutputDir(ev:InvokeEvent):String {
			var d:File;
			if(ev.arguments.length > 1) {
				outputDirPath = ev.arguments[1];
				d = new File(ev.currentDirectory.nativePath);
				d = d.resolvePath(outputDirPath);
				if(!d.isDirectory) {
					//outdir not a directory
					exit(4);
					return "";
				}
				outfield.appendText("\ninpt: " + d.nativePath);
				return d.nativePath;
			}
			else {
				if(ev.currentDirectory.nativePath === '/') {
					if(ev.arguments.length) {
						d = new File(ev.arguments[0]);
						d = d.resolvePath('..');
						return d.nativePath;
					}
					else {
						return File.desktopDirectory.nativePath;
					}
				}
				else {
					outfield.appendText("\ncwd: " + ev.currentDirectory.nativePath);
					return ev.currentDirectory.nativePath;
				}
				return "";
			}
		}
		//Invoke handler called when started
		private function onInvoke(ev:InvokeEvent):void {

			inputFilePath = getInputFile(ev);
			outputDirPath = getOutputDir(ev);

			outfield.appendText("\nInput file: " + inputFilePath);
			outfield.appendText("\nOutput directory: " + outputDirPath);
			loadSwf();
		}

		private function exit(code:int=0):void {
			NativeApplication.nativeApplication.exit(code);
		}
	}
}

package
{
	import com.adobe.images.PNGEncoder;

	import flash.desktop.NativeApplication;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.InvokeEvent;
	import flash.events.TimerEvent;
	import flash.filesystem.File;
	import flash.filesystem.FileStream;
	import flash.geom.Rectangle;
	import flash.geom.Matrix;
	import flash.net.URLRequest;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.utils.ByteArray;
	import flash.utils.Timer;

	public class swf2png extends Sprite
	{
		public var outputWidth:int;
		public var outputHeight:int;
		public var loadedSwf:MovieClip;
		public var loader:Loader;
		private var counter:int = 0;
		private var timer:Timer;
		private var totalFrames:int;
		private var inputFileName:String;
		private var inputFilePath:String;
		private var prefix:String;
		private var outfield:TextField;
		private var outputDirPath:String;
		private var offsetMatrix:Matrix;
		private var bBox:Rectangle;

		public function swf2png() {
			NativeApplication.nativeApplication.addEventListener(InvokeEvent.INVOKE, onInvoke);
			outfield = new TextField();
			outfield.autoSize = TextFieldAutoSize.LEFT;
			stage.addChild(outfield);
			stage.frameRate = 12;
		}

		//Loads in file
		private function loadSwf():void {

			loader = new Loader();
			outfield.appendText("\nLoading " + inputFilePath);
			loader.load(new URLRequest("file://" + inputFilePath));
			loader.contentLoaderInfo.addEventListener(Event.INIT, startLoop);

		}

		//Event handler called when the swf is loaded. Sets it up and starts the export loop
		private function startLoop(ev:Event):void {
			loadedSwf = MovieClip(ev.target.content);
			outputWidth = Math.ceil(ev.target.width);
			outputHeight = Math.ceil(ev.target.height);
			outfield.appendText("\nLoaded!");
			totalFrames = loadedSwf.totalFrames;

			calculateBBox();

			stopClip(loadedSwf);
			goToFrame(loadedSwf, 0);
			timer = new Timer(1);

			timer.addEventListener(TimerEvent.TIMER, step);
			timer.start();

		}

		private function calculateBBox():void {
			stopClip(loadedSwf);
			goToFrame(loadedSwf, 0);

			var boundsArray:Object = {
				"x" : new Array(),
				"y" : new Array(),
				"w" : new Array(),
				"h" : new Array()
			}
			var r:Rectangle;
			for(var currentFrame:int = 0; currentFrame < totalFrames; currentFrame++) {
				goToFrame(loadedSwf, currentFrame);
				r = loader.content.getRect(stage);
				boundsArray.x.push(r.x);
				boundsArray.y.push(r.y);
				boundsArray.w.push(r.width);
				boundsArray.h.push(r.height);
			}

			boundsArray.x.sort(Array.NUMERIC);
			boundsArray.y.sort(Array.NUMERIC);
			boundsArray.w.sort(Array.NUMERIC | Array.DESCENDING);
			boundsArray.h.sort(Array.NUMERIC | Array.DESCENDING);

			bBox = new Rectangle(
				Math.floor(boundsArray.x[0]),
				Math.floor(boundsArray.y[0]),
				Math.ceil(boundsArray.w[0]),
				Math.ceil(boundsArray.h[0])
			);

			outfield.appendText("\nBounding box: " + bBox)

			outputWidth = bBox.width;
			outputHeight = bBox.height;
			offsetMatrix = new Matrix();
			offsetMatrix.translate(bBox.x * -1, bBox.y * -1);

			return;
		}

		//Called for every frame
		private function step(ev:TimerEvent):void {
			goToFrame(loadedSwf, counter);
			saveFrame();
			counter++;
			if(counter > totalFrames) {
				timer.stop();
				exit(0);
				return;
			}
		}

		//Saves the current frame of the loader object to a png
		private function saveFrame():void {
			var bitmapData:BitmapData = new BitmapData(outputWidth, outputHeight, true, 0x0);
			bitmapData.draw(loader.content, offsetMatrix);
			var bytearr:ByteArray = PNGEncoder.encode(bitmapData);
			var outfileName:String = outputDirPath + File.separator + prefix + counter + ".png"
			var file:File = new File(outfileName);
			outfield.appendText("\nPrefix: " + prefix);
			outfield.appendText("\nWriting: " + outfileName);
			var stream:FileStream = new FileStream();
			stream.open(file, "write");
			stream.writeBytes(bytearr);
			stream.close();
		}

		//Stops the movie clip and all its subclips.
		private function stopClip(inMc:MovieClip):void {
			var l:int = inMc.numChildren;
			for (var i:int = 0; i < l; i++)
			{
				var mc:MovieClip = inMc.getChildAt(i) as MovieClip;
				if(mc) {
					mc.stop();
					if(mc.numChildren > 0) {
						stopClip(mc);
					}
				}
			}
			inMc.stop();
		}

		//Traverses the movie clip and sets the current frame for all subclips too, looping them where needed.
		private function goToFrame(inMc:MovieClip, frameNo:int):void {
			var l:int = inMc.numChildren;
			for (var i:int = 0; i < l; i++)
			{
				var mc:MovieClip = inMc.getChildAt(i) as MovieClip;
				if(mc) {
					mc.gotoAndStop(frameNo % inMc.totalFrames);
					if(mc.numChildren > 0) {
						goToFrame(mc, frameNo);
					}
				}
			}
			inMc.gotoAndStop(frameNo % inMc.totalFrames);
		}

		//Finds and checks for existance of input file
		private function getInputFile(ev:InvokeEvent):String {
			if(ev.arguments && ev.arguments.length) {
				inputFileName = ev.arguments[0];
				var matches:Array = inputFileName.match(/([a-zA-Z0-9_\-\+\.]*?)\.swf$/);
				if(!matches) {
					// File inputFileName not valid
					exit(2);
					return "";
				}
				prefix = matches[1];
				var f:File = new File(ev.currentDirectory.nativePath);
				f = f.resolvePath(inputFileName);
				if(!f.exists) {
					outfield.appendText("\nInput file not found!");
					//Input file not found
					exit(3);
					return "";
				}
				return f.nativePath;
			}
			else {
				//App opened without input data
				exit(1);
				return "";
			}
		}

		//Finds and checks for existance of output directory
		private function getOutputDir(ev:InvokeEvent):String {
			var d:File;
			if(ev.arguments.length > 1) {
				outputDirPath = ev.arguments[1];
				d = new File(ev.currentDirectory.nativePath);
				d = d.resolvePath(outputDirPath);
				if(!d.isDirectory) {
					//outdir not a directory
					exit(4);
					return "";
				}
				outfield.appendText("\ninpt: " + d.nativePath);
				return d.nativePath;
			}
			else {
				if(ev.currentDirectory.nativePath === '/') {
					if(ev.arguments.length) {
						d = new File(ev.arguments[0]);
						d = d.resolvePath('..');
						return d.nativePath;
					}
					else {
						return File.desktopDirectory.nativePath;
					}
				}
				else {
					outfield.appendText("\ncwd: " + ev.currentDirectory.nativePath);
					return ev.currentDirectory.nativePath;
				}
				return "";
			}
		}
		//Invoke handler called when started
		private function onInvoke(ev:InvokeEvent):void {

			inputFilePath = getInputFile(ev);
			outputDirPath = getOutputDir(ev);

			outfield.appendText("\nInput file: " + inputFilePath);
			outfield.appendText("\nOutput directory: " + outputDirPath);
			loadSwf();
		}

		private function exit(code:int=0):void {
			NativeApplication.nativeApplication.exit(code);
		}
	}
}

Adjust output size and positioning after content.

The output size is now adjusted after the content size, which stops content from being incorrectly cropped.


/**
 WvNetConnection
 version 1.1
 03/08/2010
 Widevine extension to the NetConnection class.  Required to stream encrypted content.
**/

package com.widevine
{
	import flash.events.*;
	import flash.external.ExternalInterface;
	import flash.net.NetConnection;
	import flash.net.Responder;

	public class WvNetConnection extends NetConnection
	{
		// URL passed in by swf
		private var myOrigURL:String;
		// URL used during connect() call.
		private var myNewURL:String;
		private var myMovie:String;
		private var myErrorText:String;
		private var myIsConnected:Boolean;
		private var myMediaTime:Number;
		private var myPlayScale:Number;
		// bypass Widevine client
		private var myIsBypassMode:Boolean;
		// progressive download
		private var myIsPdl:Boolean;

		public static const DO_CONNECT_FAILED:String = "doConnectFailed";

		public function WvNetConnection():void
		{
			this.objectEncoding = flash.net.ObjectEncoding.AMF0;
			myIsConnected 	= false;
			myIsBypassMode 	= false;
			myIsPdl	   		= true;
			myMediaTime 	= 0;
			myPlayScale 	= 1;
		}
		///////////////////////////////////////////////////////////////////////////
		public override function connect(command:String, ... arguments):void
		{
			// handle RTMP streaming, not supported in Widevine yet
			if (command.substr(0, 4) == "rtmp") {
				myIsPdl = false;
			}

			if (IsBypassMode()) {
				try {
					if (IsPdl()) {
						//trace("(bypass) Handling HTTP stream:" + command);
						super.connect(null);
						myNewURL = command;
					}
					else {
						// RTMP streaming
						//trace("(bypass) Handling RTMP stream:" + command);
						myNewURL = command.substring(0, command.lastIndexOf("/")+1);
						super.connect(myNewURL);
					}
				}
				catch (e:Error) {
					//dispatchEvent(new NetStatusEvent("onNetStatus", obj));
					//trace("WvNetStream.connect() error:" + e.message);
					throw new Error("connect() failed");
				}
				return;
			}

			// Widevine encrypted stream only
			if (IsPdl()) {
				//trace("(Handling Wv HTTP stream:" + command);
			}
			else {
				command = command.substring(0, command.lastIndexOf("/")+1);
				//trace("(Handling Wv RTMP stream:" + command);
			}
			if (doConnect(command) != 0) {
				//dispatchEvent(new NetStatusEvent("netStatus", obj));
				//throw new Error("doConnect() failed");
				dispatchEvent(new Event(DO_CONNECT_FAILED));
			}
		}
		///////////////////////////////////////////////////////////////////////////
		private function doConnect(theURL:String):Number
		{
			myOrigURL = theURL;
			if (myOrigURL == null) {
				myErrorText = "url passed in connect() is null";
				return 1;
			}

			try {
				myMovie = theURL.substr(myOrigURL.lastIndexOf("/")+1);
				myNewURL = String(ExternalInterface.call("WVGetURL", myOrigURL));

				if (myNewURL.toLowerCase().indexOf("error:") != -1) {
					myErrorText = myNewURL;
					return 2;
				}
			}
			catch (errObject:Error) {
				myErrorText = "WVGetURL() failed. " + errObject.message;
			}
			try {
				//trace("Calling super.connect()");
				super.connect(myNewURL);
			}
			catch (errObject:Error) {
				myErrorText = "super.connect() failed. " + errObject.message;
			}
			myIsConnected = true;
			return 0;
		}
		///////////////////////////////////////////////////////////////////////////
		public override function close():void
		{
			trace("WvNetConnection.close()");
			myIsConnected = false;
			super.close();
		}
		///////////////////////////////////////////////////////////////////////////
		public function getErrorText():String
		{
			return myErrorText;
		}
		///////////////////////////////////////////////////////////////////////////
		public function getNewURL():String
		{
			return myNewURL;
		}
		///////////////////////////////////////////////////////////////////////////
		public function isConnected():Boolean
		{
			return myIsConnected;
		}
		///////////////////////////////////////////////////////////////////////////
		public function setBypassMode(flag:Boolean):void
		{
			myIsBypassMode = flag;
		}
		///////////////////////////////////////////////////////////////////////////
		public function IsBypassMode():Boolean
		{
			return myIsBypassMode;
		}
		///////////////////////////////////////////////////////////////////////////
		public function IsPdl():Boolean
		{
			return myIsPdl;
		}
	}  // class
}  // package

/**
 WvNetConnection
 version 1.1
 03/08/2010
 Widevine extension to the NetConnection class.  Required to stream encrypted content.
**/

package com.widevine
{
	import flash.events.*;
	import flash.external.ExternalInterface;
	import flash.net.NetConnection;
	import flash.net.Responder;
	import flash.utils.setTimeout;

	public class WvNetConnection extends NetConnection
	{
		// URL passed in by swf
		private var myOrigURL:String;
		// URL used during connect() call.
		private var myNewURL:String;
		private var myMovie:String;
		private var myErrorText:String;
		private var myIsConnected:Boolean;
		private var myMediaTime:Number;
		private var myPlayScale:Number;
		// bypass Widevine client
		private var myIsBypassMode:Boolean;
		// progressive download
		private var myIsPdl:Boolean;

		public static const DO_CONNECT_FAILED:String = "doConnectFailed";

		public function WvNetConnection():void
		{
			this.objectEncoding = flash.net.ObjectEncoding.AMF0;
			myIsConnected 	= false;
			myIsBypassMode 	= false;
			myIsPdl	   		= true;
			myMediaTime 	= 0;
			myPlayScale 	= 1;
		}
		///////////////////////////////////////////////////////////////////////////
		public override function connect(command:String, ... arguments):void
		{
			// handle RTMP streaming, not supported in Widevine yet
			if (command.substr(0, 4) == "rtmp") {
				myIsPdl = false;
			}

			if (IsBypassMode()) {
				try {
					if (IsPdl()) {
						//trace("(bypass) Handling HTTP stream:" + command);
						super.connect(null);
						myNewURL = command;
					}
					else {
						// RTMP streaming
						//trace("(bypass) Handling RTMP stream:" + command);
						myNewURL = command.substring(0, command.lastIndexOf("/")+1);
						super.connect(myNewURL);
					}
				}
				catch (e:Error) {
					//dispatchEvent(new NetStatusEvent("onNetStatus", obj));
					//trace("WvNetStream.connect() error:" + e.message);
					throw new Error("connect() failed");
				}
				return;
			}

			// Widevine encrypted stream only
			if (IsPdl()) {
				//trace("(Handling Wv HTTP stream:" + command);
			}
			else {
				command = command.substring(0, command.lastIndexOf("/")+1);
				//trace("(Handling Wv RTMP stream:" + command);
			}
			if (doConnect(command) != 0) {
				setTimeout(function():void{
					if (doConnect(command) != 0) {
						//dispatchEvent(new NetStatusEvent("netStatus", obj));
						//throw new Error("doConnect() failed");
						dispatchEvent(new Event(DO_CONNECT_FAILED));
					}
				},1500);
			}
		}
		///////////////////////////////////////////////////////////////////////////
		private function doConnect(theURL:String):Number
		{
			myOrigURL = theURL;
			if (myOrigURL == null) {
				myErrorText = "url passed in connect() is null";
				return 1;
			}

			try {
				myMovie = theURL.substr(myOrigURL.lastIndexOf("/")+1);
				myNewURL = String(ExternalInterface.call("WVGetURL", myOrigURL));

				if (myNewURL.toLowerCase().indexOf("error:") != -1) {
					myErrorText = myNewURL;
					return 2;
				}
			}
			catch (errObject:Error) {
				myErrorText = "WVGetURL() failed. " + errObject.message;
			}
			try {
				//trace("Calling super.connect()");
				super.connect(myNewURL);
			}
			catch (errObject:Error) {
				myErrorText = "super.connect() failed. " + errObject.message;
			}
			myIsConnected = true;
			return 0;
		}
		///////////////////////////////////////////////////////////////////////////
		public override function close():void
		{
			trace("WvNetConnection.close()");
			myIsConnected = false;
			super.close();
		}
		///////////////////////////////////////////////////////////////////////////
		public function getErrorText():String
		{
			return myErrorText;
		}
		///////////////////////////////////////////////////////////////////////////
		public function getNewURL():String
		{
			return myNewURL;
		}
		///////////////////////////////////////////////////////////////////////////
		public function isConnected():Boolean
		{
			return myIsConnected;
		}
		///////////////////////////////////////////////////////////////////////////
		public function setBypassMode(flag:Boolean):void
		{
			myIsBypassMode = flag;
		}
		///////////////////////////////////////////////////////////////////////////
		public function IsBypassMode():Boolean
		{
			return myIsBypassMode;
		}
		///////////////////////////////////////////////////////////////////////////
		public function IsPdl():Boolean
		{
			return myIsPdl;
		}
	}  // class
}  // package

fix widevine issue on MAC

fix widevine issue on MAC


package dolly {
public class Cloner {
	public function Cloner() {
	}
}
}

package dolly {
public class Cloner {
	public static function clone(source:*):* {
		return null;
	}
}
}

Add empty "clone" method to Cloner class.


//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2011 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.io {

import flash.utils.ByteArray;
import flash.utils.Dictionary;

import com.threerings.util.ByteEnum;
import com.threerings.util.ClassUtil;
import com.threerings.util.Enum;

import com.threerings.io.streamers.ArrayStreamer;
import com.threerings.io.streamers.ByteArrayStreamer;
import com.threerings.io.streamers.ByteEnumStreamer;
import com.threerings.io.streamers.EnumStreamer;
import com.threerings.io.streamers.MapStreamer;
import com.threerings.io.streamers.NumberStreamer;
import com.threerings.io.streamers.SetStreamer;
import com.threerings.io.streamers.StringStreamer;

public class Streamer
{
    public static function getStreamer (obj :Object) :Streamer
    {
        var jname :String;
        if (obj is TypedArray) {
            jname = TypedArray(obj).getJavaType();
        } else {
            jname = Translations.getToServer(ClassUtil.getClassName(obj));
        }
        return getStreamerByJavaName(jname);
    }

    public static function getStreamerByJavaName (jname :String) :Streamer
    {
        initStreamers();

        // see if we have a streamer for it
        var streamer :Streamer = _byJName[jname] as Streamer;
        if (streamer != null) {
            return streamer;
        }

        // see if it's an array that we unstream using an ArrayStreamer
        if (jname.charAt(0) === "[") {
            streamer = new ArrayStreamer(jname);

        } else {
            // otherwise see if it represents a Streamable
            // usually this is called from ObjectInputStream, but when it's called from
            // ObjectOutputStream it's a bit annoying, because we started with a class/object.
            // But: the code is smaller, so that wins
            var clazz :Class = ClassUtil.getClassByName(Translations.getFromServer(jname));

            if (ClassUtil.isAssignableAs(Enum, clazz)) {
                streamer = ClassUtil.isAssignableAs(ByteEnum, clazz) ?
                    new ByteEnumStreamer(clazz, jname) : new EnumStreamer(clazz, jname);

            } else if (ClassUtil.isAssignableAs(Streamable, clazz)) {
                streamer = new Streamer(clazz, jname);

            } else {
                return null;
            }
        }

        // add the good new streamer
        registerStreamer(streamer);
        return streamer;
    }

    /** This should be a protected constructor. */
    public function Streamer (targ :Class, jname :String = null)
        //throws IOError
    {
        _target = targ;
        _jname = (jname != null) ? jname : Translations.getToServer(ClassUtil.getClassName(targ));
    }

    /**
     * Return the String to use to identify the class that we're streaming.
     */
    public function getJavaClassName () :String
    {
        return _jname;
    }

    public function writeObject (obj :Object, out :ObjectOutputStream) :void
        //throws IOError
    {
        (obj as Streamable).writeObject(out);
    }

    public function createObject (ins :ObjectInputStream) :Object
        //throws IOError
    {
        // actionscript is so fucked up
        return new _target();
    }

    public function readObject (obj :Object, ins :ObjectInputStream) :void
        //throws IOError
    {
        (obj as Streamable).readObject(ins);
    }

    public function toString () :String
    {
        return "[Streamer(" + _jname + ")]";
    }

    protected static function registerStreamer (st :Streamer, ... extraJavaNames) :void
    {
        _byJName[st.getJavaClassName()] = st;
        for each (var name :String in extraJavaNames) {
            _byJName[name] = st;
        }
    }

    /**
     * Initialize our streamers. This cannot simply be done statically
     * because we cannot instantiate a subclass when this class is still
     * being created. Fucking actionscript.
     */
    private static function initStreamers () :void
    {
        if (_byJName != null) {
            return;
        }
        _byJName = new Dictionary();
        for each (var c :Class in [ StringStreamer, NumberStreamer, ByteArrayStreamer ]) {
            registerStreamer(Streamer(new c()));
        }
        registerStreamer(ArrayStreamer.INSTANCE,
            "java.util.List", "java.util.ArrayList", "java.util.Collection");
        registerStreamer(SetStreamer.INSTANCE, "java.util.Set");
        registerStreamer(MapStreamer.INSTANCE, "java.util.Map");
    }

    protected var _target :Class;

    protected var _jname :String;

    protected static var _byJName :Dictionary;
}
}

//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2011 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.io {

import com.threerings.io.streamers.ArrayStreamer;
import com.threerings.io.streamers.ByteArrayStreamer;
import com.threerings.io.streamers.ByteEnumStreamer;
import com.threerings.io.streamers.EnumStreamer;
import com.threerings.io.streamers.MapStreamer;
import com.threerings.io.streamers.NumberStreamer;
import com.threerings.io.streamers.SetStreamer;
import com.threerings.io.streamers.StringStreamer;
import com.threerings.util.ByteEnum;
import com.threerings.util.ClassUtil;
import com.threerings.util.Enum;
import com.threerings.util.Log;

import flash.utils.ByteArray;
import flash.utils.Dictionary;

public class Streamer
{
    public static function registerStreamer (st :Streamer, ... extraJavaNames) :void
    {
        initStreamers();

        extraJavaNames.unshift(st.getJavaClassName());
        for each (var name :String in extraJavaNames) {
            if (name in _byJName) {
                log.warning("Existing Streamer replaced", "name", name,
                    "cur", _byJName[name], "replacement", st);
            }
            _byJName[name] = st;
        }
    }

    public static function getStreamer (obj :Object) :Streamer
    {
        var jname :String;
        if (obj is TypedArray) {
            jname = TypedArray(obj).getJavaType();
        } else {
            jname = Translations.getToServer(ClassUtil.getClassName(obj));
        }
        return getStreamerByJavaName(jname);
    }

    public static function getStreamerByJavaName (jname :String) :Streamer
    {
        initStreamers();

        // see if we have a streamer for it
        var streamer :Streamer = _byJName[jname] as Streamer;
        if (streamer != null) {
            return streamer;
        }

        // see if it's an array that we unstream using an ArrayStreamer
        if (jname.charAt(0) === "[") {
            streamer = new ArrayStreamer(jname);

        } else {
            // otherwise see if it represents a Streamable
            // usually this is called from ObjectInputStream, but when it's called from
            // ObjectOutputStream it's a bit annoying, because we started with a class/object.
            // But: the code is smaller, so that wins
            var clazz :Class = ClassUtil.getClassByName(Translations.getFromServer(jname));

            if (ClassUtil.isAssignableAs(Enum, clazz)) {
                streamer = ClassUtil.isAssignableAs(ByteEnum, clazz) ?
                    new ByteEnumStreamer(clazz, jname) : new EnumStreamer(clazz, jname);

            } else if (ClassUtil.isAssignableAs(Streamable, clazz)) {
                streamer = new Streamer(clazz, jname);

            } else {
                return null;
            }
        }

        // add the good new streamer
        registerStreamer(streamer);
        return streamer;
    }

    /** This should be a protected constructor. */
    public function Streamer (targ :Class, jname :String = null)
        //throws IOError
    {
        _target = targ;
        _jname = (jname != null) ? jname : Translations.getToServer(ClassUtil.getClassName(targ));
    }

    /**
     * Return the String to use to identify the class that we're streaming.
     */
    public function getJavaClassName () :String
    {
        return _jname;
    }

    public function writeObject (obj :Object, out :ObjectOutputStream) :void
        //throws IOError
    {
        (obj as Streamable).writeObject(out);
    }

    public function createObject (ins :ObjectInputStream) :Object
        //throws IOError
    {
        // actionscript is so fucked up
        return new _target();
    }

    public function readObject (obj :Object, ins :ObjectInputStream) :void
        //throws IOError
    {
        (obj as Streamable).readObject(ins);
    }

    public function toString () :String
    {
        return "[Streamer(" + _jname + ")]";
    }

    /**
     * Initialize our streamers. This cannot simply be done statically
     * because we cannot instantiate a subclass when this class is still
     * being created. Fucking actionscript.
     */
    private static function initStreamers () :void
    {
        if (_byJName != null) {
            return;
        }
        _byJName = new Dictionary();

        for each (var c :Class in [ StringStreamer, NumberStreamer, ByteArrayStreamer ]) {
            registerStreamer(Streamer(new c()));
        }
        registerStreamer(ArrayStreamer.INSTANCE,
            "java.util.List", "java.util.ArrayList", "java.util.Collection");
        registerStreamer(SetStreamer.INSTANCE, "java.util.Set");
        registerStreamer(MapStreamer.INSTANCE, "java.util.Map");
    }

    protected var _target :Class;

    protected var _jname :String;

    protected static var _byJName :Dictionary;

    protected static const log :Log = Log.getLog(Streamer);
}
}

Make Streamer.registerStreamer public, and add a warning if an existing Streamer is replaced.

(In Who, we need to stream com.threerings.geom.Vector2, so we need a custom Streamer for it)

git-svn-id: a1a4b28b82a3276cc491891159dd9963a0a72fae@6487 542714f4-19e9-0310-aa3c-eee0fc999fb1


package com.kaltura.hls.m2ts
{
	import com.kaltura.hls.HLSStreamingResource;
	import com.kaltura.hls.SubtitleTrait;
	import com.kaltura.hls.manifest.HLSManifestEncryptionKey;
	import com.kaltura.hls.muxing.AACParser;
	import com.kaltura.hls.subtitles.SubTitleParser;
	import com.kaltura.hls.subtitles.TextTrackCue;
	import com.kaltura.hls.HLSIndexHandler;

	import flash.utils.ByteArray;
	import flash.utils.IDataInput;
	import flash.utils.getTimer;

	import org.osmf.events.HTTPStreamingEvent;
	import org.osmf.net.httpstreaming.HTTPStreamingFileHandlerBase;
	import org.osmf.net.httpstreaming.flv.FLVTagAudio;

	/**
	 * Process M2TS data into FLV data and return it for rendering via OSMF video system.
	 */
	public class M2TSFileHandler extends HTTPStreamingFileHandlerBase
	{
		public var subtitleTrait:SubtitleTrait;
		public var key:HLSManifestEncryptionKey;
		public var segmentId:uint = 0;
		public var resource:HLSStreamingResource;
		public var segmentUri:String;
		public var isBestEffort:Boolean = false;

		private var _parser:TSPacketParser;
		private var _curTimeOffset:uint;
		private var _buffer:ByteArray;
		private var _fragReadBuffer:ByteArray;
		private var _encryptedDataBuffer:ByteArray;
		private var _timeOrigin:uint;
		private var _timeOriginNeeded:Boolean;
		private var _segmentBeginSeconds:Number;
		private var _segmentLastSeconds:Number;
		private var _firstSeekTime:Number;
		private var _lastContinuityToken:String;
		private var _extendedIndexHandler:IExtraIndexHandlerState;
		private var _lastFLVMessageTime:Number;
		private var _injectingSubtitles:Boolean = false;
		private var _lastInjectedSubtitleTime:Number = 0;

		private var _decryptionIV:ByteArray;

		public function M2TSFileHandler()
		{
			super();

			_encryptedDataBuffer = new ByteArray();

			_parser = new TSPacketParser();
			_parser.callback = handleFLVMessage;

			_timeOrigin = 0;
			_timeOriginNeeded = true;

			_segmentBeginSeconds = -1;
			_segmentLastSeconds = -1;

			_firstSeekTime = 0;

			_extendedIndexHandler = null;

			_lastContinuityToken = null;
		}

		public function get duration():Number
		{
			if(_segmentLastSeconds > _segmentBeginSeconds)
				return _segmentLastSeconds - _segmentBeginSeconds;
			return -1;
		}

		public function set extendedIndexHandler(handler:IExtraIndexHandlerState):void
		{
			_extendedIndexHandler = handler;
		}

		public function get extendedIndexHandler():IExtraIndexHandlerState
		{
			return _extendedIndexHandler;
		}

		public override function beginProcessFile(seek:Boolean, seekTime:Number):void
		{
			// Decryption reset
			if ( key )
			{
				if ( key.iv ) _decryptionIV = key.retrieveStoredIV();
				else _decryptionIV = HLSManifestEncryptionKey.createIVFromID( segmentId );
			}

			var discontinuity:Boolean = false;

			if(_extendedIndexHandler)
			{
				var currentContinuityToken:String = _extendedIndexHandler.getCurrentContinuityToken();

				if(_lastContinuityToken != currentContinuityToken)
					discontinuity = true;
				_lastContinuityToken = currentContinuityToken;
			}

			if(seek)
			{
				_parser.clear();

				_timeOriginNeeded = true;

				if(_extendedIndexHandler)
					_firstSeekTime = _extendedIndexHandler.calculateFileOffsetForTime(seekTime) * 1000.0;
			}
			else if(discontinuity)
			{
				// Kick the converter state, but try to avoid upsetting the audio stream.
				_parser.clear(false);

				if(_segmentLastSeconds >= 0.0)
				{
					_timeOriginNeeded = true;
					if(_extendedIndexHandler)
						_firstSeekTime = _extendedIndexHandler.getCurrentSegmentOffset() * 1000.0;
					else
						_firstSeekTime = _segmentLastSeconds * 1000.0 + 30;
				}
			}
			else if(_extendedIndexHandler && _segmentLastSeconds >= 0.0)
			{
				var currentFileOffset:Number = _extendedIndexHandler.getCurrentSegmentOffset();
				var delta:Number = currentFileOffset - _segmentLastSeconds;

				// If it's a big jump, handle it.
				if(delta > 5.0)
				{
					_timeOriginNeeded = true;
					_firstSeekTime = currentFileOffset * 1000.0;
				}
			}

			_segmentBeginSeconds = -1;
			_segmentLastSeconds = -1;
			_lastInjectedSubtitleTime = -1;
		}

		public override function get inputBytesNeeded():Number
		{
			// We can always survive no bytes.
			return 0;
		}

		public static var tmpBuffer:ByteArray = new ByteArray();

		private function basicProcessFileSegment(input:IDataInput, flush:Boolean):ByteArray
		{
			if ( key && !key.isLoaded )
			{
				input.readBytes( _encryptedDataBuffer, _encryptedDataBuffer.length );
				return null;
			}

			tmpBuffer.position = 0;
			tmpBuffer.length = 0;

			if ( _encryptedDataBuffer.length > 0 )
			{
				_encryptedDataBuffer.position = 0;
				_encryptedDataBuffer.readBytes( tmpBuffer );
				_encryptedDataBuffer.clear();
			}

			input.readBytes( tmpBuffer, tmpBuffer.length );

			if ( key )
			{
				var bytesToRead:uint = tmpBuffer.length;
				var leftoverBytes:uint = bytesToRead % 16;
				bytesToRead -= leftoverBytes;

				key.usePadding = false;

				if ( leftoverBytes > 0 )
				{
					// Place any bytes left over (not divisible by 16) into our encrypted buffer
					// to decrypt later, when we have more bytes
					tmpBuffer.position = bytesToRead;
					tmpBuffer.readBytes( _encryptedDataBuffer );
					tmpBuffer.length = bytesToRead;
				}
				else
				{
					// Attempt to unpad if our buffer is equally divisible by 16.
					// It could mean that we've reached the end of the file segment.
					 key.usePadding = true;
				}

				// Store our current IV so we can use it do decrypt
				var currentIV:ByteArray = _decryptionIV;

				// Set up the IV for our next set of bytes
				_decryptionIV = new ByteArray();
				tmpBuffer.position = bytesToRead - 16;
				tmpBuffer.readBytes( _decryptionIV );

				// Aaaaand...decrypt!
				key.decrypt( tmpBuffer, currentIV );
			}

			// If it's AAC, process it.
			if(AACParser.probe(tmpBuffer))
			{
				//trace("GOT AAC " + tmpBuffer.bytesAvailable);
				var aac:AACParser = new AACParser();
				aac.parse(tmpBuffer, _fragReadHandler);
				//trace("    - returned " + _fragReadBuffer.length + " bytes!");
				_fragReadBuffer.position = 0;

				if(isBestEffort && _fragReadBuffer.length > 0)
				{
					trace("Discarding AAC data from best effort.");
					_fragReadBuffer.length = 0;
				}

				return _fragReadBuffer;
			}

			var buffer:ByteArray = new ByteArray();
			_buffer = buffer;
			_parser.appendBytes(tmpBuffer);
			if ( flush ) _parser.flush();
			_buffer = null;
			buffer.position = 0;

			if(isBestEffort && buffer.length > 0)
			{
				trace("Discarding normal data from best effort.");
				buffer.length = 0;
			}

			return buffer;
		}

		private function _fragReadHandler(audioTags:Vector.<FLVTagAudio>, adif:ByteArray):void
		{
			_fragReadBuffer = new ByteArray();
			var audioTag:FLVTagAudio = new FLVTagAudio();
			audioTag.soundFormat = FLVTagAudio.SOUND_FORMAT_AAC;
			audioTag.data = adif;
			audioTag.isAACSequenceHeader = true;
			audioTag.write(_fragReadBuffer);

			for(var i:int=0; i<audioTags.length; i++)
				audioTags[i].write(_fragReadBuffer);
		}

		public override function processFileSegment(input:IDataInput):ByteArray
		{
			return basicProcessFileSegment(input, false);
		}

		public override function endProcessFile(input:IDataInput):ByteArray
		{
			if ( key ) key.usePadding = true;

			var rv:ByteArray = basicProcessFileSegment(input, false);

			var elapsed:Number = _segmentLastSeconds - _segmentBeginSeconds;

			if(elapsed <= 0.0 && _extendedIndexHandler)
			{
				elapsed = _extendedIndexHandler.getTargetSegmentDuration(); // XXX fudge hack!
			}

			dispatchEvent(new HTTPStreamingEvent(HTTPStreamingEvent.FRAGMENT_DURATION, false, false, elapsed));

			return rv;
		}

		public override function flushFileSegment(input:IDataInput):ByteArray
		{
			return basicProcessFileSegment(input || new ByteArray(), true);
		}

		private function handleFLVMessage(timestamp:uint, message:ByteArray):void
		{
			var timestampSeconds:Number = timestamp / 1000.0;

			if(_segmentBeginSeconds < 0)
			{
				_segmentBeginSeconds = timestampSeconds;
				trace("Noting segment start time for " + segmentUri + " of " + timestampSeconds);
				HLSIndexHandler.startTimeWitnesses[segmentUri] = timestampSeconds;
			}

			if(timestampSeconds > _segmentLastSeconds)
				_segmentLastSeconds = timestampSeconds;

			if(isBestEffort)
				return;

			//trace("Got " + message.length + " bytes at " + timestampSeconds + " seconds");

			if(_timeOriginNeeded)
			{
				_timeOrigin = timestamp;
				_timeOriginNeeded = false;
			}

			if(timestamp < _timeOrigin)
				_timeOrigin = timestamp;

			// Encode the timestamp.
			message[6] = (timestamp      ) & 0xff;
			message[5] = (timestamp >>  8) & 0xff;
			message[4] = (timestamp >> 16) & 0xff;
			message[7] = (timestamp >> 24) & 0xff;

			var lastMsgTime:Number = _lastFLVMessageTime;
			_lastFLVMessageTime = timestampSeconds;

			// If timer was reset due to seek, reset last subtitle time
			if(timestampSeconds < _lastInjectedSubtitleTime)
			{
				trace("Bumping back on subtitle threshold.")
				_lastInjectedSubtitleTime = timestampSeconds;
			}

			// Inject any subtitle tags between messages
			injectSubtitles( _lastInjectedSubtitleTime + 0.001, timestampSeconds );

			//trace( "MESSAGE RECEIVED " + timestampSeconds );

			_buffer.writeBytes(message);
		}

		protected var _lastCue:TextTrackCue = null;

		private function injectSubtitles( startTime:Number, endTime:Number ):void
		{
			//if(startTime > endTime) trace("***** BAD BEHAVIOR " + startTime + " " + endTime);

			//trace("Inject subtitles " + startTime + " " + endTime);

			// Early out if no subtitles, no time has elapsed or we are already injecting subtitles
			if ( !subtitleTrait || endTime - startTime <= 0 || _injectingSubtitles ) return;

			var subtitles:Vector.<SubTitleParser> = subtitleTrait.activeSubtitles;
			if ( !subtitles ) return;

			_injectingSubtitles = true;

			var subtitleCount:int = subtitles.length;
			for ( var i:int = 0; i < subtitleCount; i++ )
			{
				var subtitle:SubTitleParser = subtitles[ i ];
				if ( subtitle.startTime > endTime ) break;
				var cues:Vector.<TextTrackCue> = subtitle.textTrackCues;
				var cueCount:int = cues.length;

				var potentials:Vector.<TextTrackCue> = new Vector.<TextTrackCue>();

				for ( var j:int = 0; j < cueCount; j++ )
				{
					var cue:TextTrackCue = cues[ j ];
					if ( cue.startTime > endTime ) break;
					else if ( cue.startTime >= startTime )
					{
						potentials.push(cue);
					}
				}

				if(potentials.length > 0)
				{
					// TODO: Add support for trackid
					cue = potentials[potentials.length - 1];
					if(cue != _lastCue)
					{
						_parser.createAndSendCaptionMessage( cue.startTime, cue.buffer, subtitleTrait.language );
						_lastInjectedSubtitleTime = cue.startTime;
						_lastCue = cue;
					}
				}
			}

			_injectingSubtitles = false;
		}
	}
}

package com.kaltura.hls.m2ts
{
	import com.kaltura.hls.HLSStreamingResource;
	import com.kaltura.hls.SubtitleTrait;
	import com.kaltura.hls.manifest.HLSManifestEncryptionKey;
	import com.kaltura.hls.muxing.AACParser;
	import com.kaltura.hls.subtitles.SubTitleParser;
	import com.kaltura.hls.subtitles.TextTrackCue;
	import com.kaltura.hls.HLSIndexHandler;

	import flash.utils.ByteArray;
	import flash.utils.IDataInput;
	import flash.utils.getTimer;

	import org.osmf.events.HTTPStreamingEvent;
	import org.osmf.net.httpstreaming.HTTPStreamingFileHandlerBase;
	import org.osmf.net.httpstreaming.flv.FLVTagAudio;

	/**
	 * Process M2TS data into FLV data and return it for rendering via OSMF video system.
	 */
	public class M2TSFileHandler extends HTTPStreamingFileHandlerBase
	{
		public var subtitleTrait:SubtitleTrait;
		public var key:HLSManifestEncryptionKey;
		public var segmentId:uint = 0;
		public var resource:HLSStreamingResource;
		public var segmentUri:String;
		public var isBestEffort:Boolean = false;

		private var _parser:TSPacketParser;
		private var _curTimeOffset:uint;
		private var _buffer:ByteArray;
		private var _fragReadBuffer:ByteArray;
		private var _encryptedDataBuffer:ByteArray;
		private var _timeOrigin:uint;
		private var _timeOriginNeeded:Boolean;
		private var _segmentBeginSeconds:Number;
		private var _segmentLastSeconds:Number;
		private var _firstSeekTime:Number;
		private var _lastContinuityToken:String;
		private var _extendedIndexHandler:IExtraIndexHandlerState;
		private var _lastFLVMessageTime:Number;
		private var _injectingSubtitles:Boolean = false;
		private var _lastInjectedSubtitleTime:Number = 0;

		private var _decryptionIV:ByteArray;

		public function M2TSFileHandler()
		{
			super();

			_encryptedDataBuffer = new ByteArray();

			_parser = new TSPacketParser();
			_parser.callback = handleFLVMessage;

			_timeOrigin = 0;
			_timeOriginNeeded = true;

			_segmentBeginSeconds = -1;
			_segmentLastSeconds = -1;

			_firstSeekTime = 0;

			_extendedIndexHandler = null;

			_lastContinuityToken = null;
		}

		public function get duration():Number
		{
			if(_segmentLastSeconds > _segmentBeginSeconds)
				return _segmentLastSeconds - _segmentBeginSeconds;
			return -1;
		}

		public function set extendedIndexHandler(handler:IExtraIndexHandlerState):void
		{
			_extendedIndexHandler = handler;
		}

		public function get extendedIndexHandler():IExtraIndexHandlerState
		{
			return _extendedIndexHandler;
		}

		public override function beginProcessFile(seek:Boolean, seekTime:Number):void
		{
			if(isBestEffort)
			{
				trace("Doing extra flush for best effort file handler");
				_parser.flush();
				_parser.clear();
			}

			// Decryption reset
			if ( key )
			{
				if ( key.iv ) _decryptionIV = key.retrieveStoredIV();
				else _decryptionIV = HLSManifestEncryptionKey.createIVFromID( segmentId );
			}

			var discontinuity:Boolean = false;

			if(_extendedIndexHandler)
			{
				var currentContinuityToken:String = _extendedIndexHandler.getCurrentContinuityToken();

				if(_lastContinuityToken != currentContinuityToken)
					discontinuity = true;
				_lastContinuityToken = currentContinuityToken;
			}

			if(seek)
			{
				_parser.clear();

				_timeOriginNeeded = true;

				if(_extendedIndexHandler)
					_firstSeekTime = _extendedIndexHandler.calculateFileOffsetForTime(seekTime) * 1000.0;
			}
			else if(discontinuity)
			{
				// Kick the converter state, but try to avoid upsetting the audio stream.
				_parser.clear(false);

				if(_segmentLastSeconds >= 0.0)
				{
					_timeOriginNeeded = true;
					if(_extendedIndexHandler)
						_firstSeekTime = _extendedIndexHandler.getCurrentSegmentOffset() * 1000.0;
					else
						_firstSeekTime = _segmentLastSeconds * 1000.0 + 30;
				}
			}
			else if(_extendedIndexHandler && _segmentLastSeconds >= 0.0)
			{
				var currentFileOffset:Number = _extendedIndexHandler.getCurrentSegmentOffset();
				var delta:Number = currentFileOffset - _segmentLastSeconds;

				// If it's a big jump, handle it.
				if(delta > 5.0)
				{
					_timeOriginNeeded = true;
					_firstSeekTime = currentFileOffset * 1000.0;
				}
			}

			_segmentBeginSeconds = -1;
			_segmentLastSeconds = -1;
			_lastInjectedSubtitleTime = -1;
		}

		public override function get inputBytesNeeded():Number
		{
			// We can always survive no bytes.
			return 0;
		}

		public static var tmpBuffer:ByteArray = new ByteArray();

		private function basicProcessFileSegment(input:IDataInput, flush:Boolean):ByteArray
		{
			if ( key && !key.isLoaded )
			{
				input.readBytes( _encryptedDataBuffer, _encryptedDataBuffer.length );
				return null;
			}

			tmpBuffer.position = 0;
			tmpBuffer.length = 0;

			if ( _encryptedDataBuffer.length > 0 )
			{
				_encryptedDataBuffer.position = 0;
				_encryptedDataBuffer.readBytes( tmpBuffer );
				_encryptedDataBuffer.clear();
			}

			input.readBytes( tmpBuffer, tmpBuffer.length );

			if ( key )
			{
				var bytesToRead:uint = tmpBuffer.length;
				var leftoverBytes:uint = bytesToRead % 16;
				bytesToRead -= leftoverBytes;

				key.usePadding = false;

				if ( leftoverBytes > 0 )
				{
					// Place any bytes left over (not divisible by 16) into our encrypted buffer
					// to decrypt later, when we have more bytes
					tmpBuffer.position = bytesToRead;
					tmpBuffer.readBytes( _encryptedDataBuffer );
					tmpBuffer.length = bytesToRead;
				}
				else
				{
					// Attempt to unpad if our buffer is equally divisible by 16.
					// It could mean that we've reached the end of the file segment.
					 key.usePadding = true;
				}

				// Store our current IV so we can use it do decrypt
				var currentIV:ByteArray = _decryptionIV;

				// Set up the IV for our next set of bytes
				_decryptionIV = new ByteArray();
				tmpBuffer.position = bytesToRead - 16;
				tmpBuffer.readBytes( _decryptionIV );

				// Aaaaand...decrypt!
				key.decrypt( tmpBuffer, currentIV );
			}

			// If it's AAC, process it.
			if(AACParser.probe(tmpBuffer))
			{
				//trace("GOT AAC " + tmpBuffer.bytesAvailable);
				var aac:AACParser = new AACParser();
				aac.parse(tmpBuffer, _fragReadHandler);
				//trace("    - returned " + _fragReadBuffer.length + " bytes!");
				_fragReadBuffer.position = 0;

				if(isBestEffort && _fragReadBuffer.length > 0)
				{
					trace("Discarding AAC data from best effort.");
					_fragReadBuffer.length = 0;
				}

				return _fragReadBuffer;
			}

			var buffer:ByteArray = new ByteArray();
			_buffer = buffer;
			_parser.appendBytes(tmpBuffer);
			if ( flush ) _parser.flush();
			_buffer = null;
			buffer.position = 0;

			if(isBestEffort && buffer.length > 0)
			{
				trace("Discarding normal data from best effort.");
				buffer.length = 0;
			}

			return buffer;
		}

		private function _fragReadHandler(audioTags:Vector.<FLVTagAudio>, adif:ByteArray):void
		{
			_fragReadBuffer = new ByteArray();
			var audioTag:FLVTagAudio = new FLVTagAudio();
			audioTag.soundFormat = FLVTagAudio.SOUND_FORMAT_AAC;
			audioTag.data = adif;
			audioTag.isAACSequenceHeader = true;
			audioTag.write(_fragReadBuffer);

			for(var i:int=0; i<audioTags.length; i++)
				audioTags[i].write(_fragReadBuffer);
		}

		public override function processFileSegment(input:IDataInput):ByteArray
		{
			return basicProcessFileSegment(input, false);
		}

		public override function endProcessFile(input:IDataInput):ByteArray
		{
			if ( key ) key.usePadding = true;

			var rv:ByteArray = basicProcessFileSegment(input, false);

			var elapsed:Number = _segmentLastSeconds - _segmentBeginSeconds;

			if(elapsed <= 0.0 && _extendedIndexHandler)
			{
				elapsed = _extendedIndexHandler.getTargetSegmentDuration(); // XXX fudge hack!
			}

			dispatchEvent(new HTTPStreamingEvent(HTTPStreamingEvent.FRAGMENT_DURATION, false, false, elapsed));

			return rv;
		}

		public override function flushFileSegment(input:IDataInput):ByteArray
		{
			return basicProcessFileSegment(input || new ByteArray(), true);
		}

		private function handleFLVMessage(timestamp:uint, message:ByteArray):void
		{
			var timestampSeconds:Number = timestamp / 1000.0;

			if(_segmentBeginSeconds < 0)
			{
				_segmentBeginSeconds = timestampSeconds;
				trace("Noting segment start time for " + segmentUri + " of " + timestampSeconds);
				HLSIndexHandler.startTimeWitnesses[segmentUri] = timestampSeconds;
			}

			if(timestampSeconds > _segmentLastSeconds)
				_segmentLastSeconds = timestampSeconds;

			if(isBestEffort)
				return;

			//trace("Got " + message.length + " bytes at " + timestampSeconds + " seconds");

			if(_timeOriginNeeded)
			{
				_timeOrigin = timestamp;
				_timeOriginNeeded = false;
			}

			if(timestamp < _timeOrigin)
				_timeOrigin = timestamp;

			// Encode the timestamp.
			message[6] = (timestamp      ) & 0xff;
			message[5] = (timestamp >>  8) & 0xff;
			message[4] = (timestamp >> 16) & 0xff;
			message[7] = (timestamp >> 24) & 0xff;

			var lastMsgTime:Number = _lastFLVMessageTime;
			_lastFLVMessageTime = timestampSeconds;

			// If timer was reset due to seek, reset last subtitle time
			if(timestampSeconds < _lastInjectedSubtitleTime)
			{
				trace("Bumping back on subtitle threshold.")
				_lastInjectedSubtitleTime = timestampSeconds;
			}

			// Inject any subtitle tags between messages
			injectSubtitles( _lastInjectedSubtitleTime + 0.001, timestampSeconds );

			//trace( "MESSAGE RECEIVED " + timestampSeconds );

			_buffer.writeBytes(message);
		}

		protected var _lastCue:TextTrackCue = null;

		private function injectSubtitles( startTime:Number, endTime:Number ):void
		{
			//if(startTime > endTime) trace("***** BAD BEHAVIOR " + startTime + " " + endTime);

			//trace("Inject subtitles " + startTime + " " + endTime);

			// Early out if no subtitles, no time has elapsed or we are already injecting subtitles
			if ( !subtitleTrait || endTime - startTime <= 0 || _injectingSubtitles ) return;

			var subtitles:Vector.<SubTitleParser> = subtitleTrait.activeSubtitles;
			if ( !subtitles ) return;

			_injectingSubtitles = true;

			var subtitleCount:int = subtitles.length;
			for ( var i:int = 0; i < subtitleCount; i++ )
			{
				var subtitle:SubTitleParser = subtitles[ i ];
				if ( subtitle.startTime > endTime ) break;
				var cues:Vector.<TextTrackCue> = subtitle.textTrackCues;
				var cueCount:int = cues.length;

				var potentials:Vector.<TextTrackCue> = new Vector.<TextTrackCue>();

				for ( var j:int = 0; j < cueCount; j++ )
				{
					var cue:TextTrackCue = cues[ j ];
					if ( cue.startTime > endTime ) break;
					else if ( cue.startTime >= startTime )
					{
						potentials.push(cue);
					}
				}

				if(potentials.length > 0)
				{
					// TODO: Add support for trackid
					cue = potentials[potentials.length - 1];
					if(cue != _lastCue)
					{
						_parser.createAndSendCaptionMessage( cue.startTime, cue.buffer, subtitleTrait.language );
						_lastInjectedSubtitleTime = cue.startTime;
						_lastCue = cue;
					}
				}
			}

			_injectingSubtitles = false;
		}
	}
}

Address issue where failing to flush could result in inaccurate best effort results.


package Controller
{
	import components.DynamicTextArea;

	public class TextController
	{

		private static var instance:TextController;

		public function TextController(singletonEnforcer:SingletonEnforcer)
		{
			//Event Listeners
		}

		public static function getInstance() :TextController{
			if(instance == null){
				instance = new TextController(new SingletonEnforcer);
			}
			return instance;
		}

		//---------------- Text Changed ------------------//
		public function updateModelText(dta:DynamicTextArea):void{
			dta.model.text = dta.text;
		}
	}
}

class SingletonEnforcer{}

package Controller
{
	import components.DynamicTextArea;

	public class TextController
	{

		private static var instance:TextController;

		public function TextController(singletonEnforcer:SingletonEnforcer)
		{
			//Event Listeners
		}

		public static function getInstance() :TextController{
			if(instance == null){
				instance = new TextController(new SingletonEnforcer);
			}
			return instance;
		}

		//---------------- Text Changed ------------------//
		public function updateModelText(dta:DynamicTextArea):void{
			dta.model.text = dta.text;
		}
		public function escapeText(s:String):String{
			s.replace(/&/g, "&amp");
			s.replace(/\"/g, "&quot");
			//s.replace(/'/g, "'"); //This does not appear to be necessary
			s.replace(/</g, "<");
			s.replace(/>/g, ">");
			s.replace(/&/, "%26");
			return s;
		}
	}
}

class SingletonEnforcer{}

Add escapeText to TextController. This should handle client-side text escaping.


package aerys.minko.render.shader
{
	import aerys.minko.ns.minko;
	import aerys.minko.render.ressource.TextureRessource;
	import aerys.minko.render.shader.compiler.DebugCompiler;
	import aerys.minko.render.shader.compiler.allocator.ParameterAllocation;
	import aerys.minko.render.shader.node.INode;
	import aerys.minko.render.shader.node.leaf.AbstractParameter;
	import aerys.minko.render.shader.node.leaf.StyleParameter;
	import aerys.minko.render.shader.node.leaf.TransformParameter;
	import aerys.minko.render.shader.node.leaf.WorldParameter;
	import aerys.minko.render.renderer.state.RenderState;
	import aerys.minko.scene.visitor.data.LocalData;
	import aerys.minko.scene.visitor.data.StyleStack;
	import aerys.minko.scene.visitor.data.LocalData;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Vector4;
	import aerys.minko.type.vertex.format.VertexComponent;

	import flash.utils.ByteArray;
	import flash.utils.Dictionary;

	public class DynamicShader extends Shader
	{
		use namespace minko;

		protected var _vsConstData	: Vector.<Number>;
		protected var _fsConstData	: Vector.<Number>;
		protected var _vsParams		: Vector.<ParameterAllocation>;
		protected var _fsParams		: Vector.<ParameterAllocation>;
		protected var _samplers		: Vector.<String>;

		public static function create(clipspacePosition	: INode,
									  color				: INode) : DynamicShader
		{
			var compiler : DebugCompiler = new DebugCompiler();

			compiler.load(clipspacePosition, color);

			return compiler.compileShader();
		}

		public function DynamicShader(vertexShader					: ByteArray,
									  fragmentShader				: ByteArray,
									  vertexInput					: Vector.<VertexComponent>,
									  vertexShaderConstantData		: Vector.<Number>,
									  fragmentShaderConstantData	: Vector.<Number>,
									  vertexShaderParameters		: Vector.<ParameterAllocation>,
									  fragmentShaderParameters		: Vector.<ParameterAllocation>,
									  samplers						: Vector.<String>)
		{
			_vsConstData	= vertexShaderConstantData;
			_fsConstData	= fragmentShaderConstantData;
			_vsParams		= vertexShaderParameters;
			_fsParams		= fragmentShaderParameters;
			_samplers		= samplers;

			super(vertexShader, fragmentShader, vertexInput);
		}

		public function fillRenderState(state	: RenderState,
										style	: StyleStack,
										local	: LocalData,
										world	: Dictionary) : Boolean
		{
			setTextures(state, style, local, world);
			setConstants(state, style, local, world);

			state.shader = this;

			return true;
		}

		protected function setTextures(state		: RenderState,
									   styleStack	: StyleStack,
								   	   localData	: LocalData,
									   worldData	: Object) : void
		{
			var texture 			: TextureRessource	= null;
			var samplerStyleName 	: String			= null;
			var samplerCount 		: uint 				= _samplers.length;

			for (var i : int = 0; i < samplerCount; ++i)
			{
				samplerStyleName = _samplers[i];
				texture = styleStack.get(samplerStyleName) as TextureRessource;
				state.setTextureAt(i, texture);
			}
		}

		protected function setConstants(state		: RenderState,
									    styleStack	: StyleStack,
										local		: LocalData,
										world		: Dictionary) : void
		{
			updateConstData(_vsConstData, _vsParams, styleStack, local, world);
			updateConstData(_fsConstData, _fsParams, styleStack, local, world);

			state.setVertexConstants(0, _vsConstData);
			state.setFragmentConstants(0, _fsConstData);
		}

		protected function updateConstData(constData	: Vector.<Number>,
										   paramsAllocs	: Vector.<ParameterAllocation>,
										   styleStack	: StyleStack,
										   local		: LocalData,
										   world		: Dictionary) : void
		{
			var paramLength	: int = paramsAllocs.length;

			for (var i : int = 0; i < paramLength; ++i)
			{
				var paramAlloc	: ParameterAllocation	= paramsAllocs[i];
				var param		: AbstractParameter		= paramAlloc._parameter;

				var data : Object = getParameterData(param, styleStack, local, world);

				loadParameterData(paramAlloc, constData, data);
			}
		}

		private function getParameterData(param			: AbstractParameter,
										  styleStack	: StyleStack,
										  local			: LocalData,
										  world			: Dictionary) : Object
		{
			if (param is StyleParameter)
			{
				if (param._index != -1)
				{
					return styleStack.get(param._key).getItem(param._index)[param._field];
				}
				else if (param._field != null)
				{
					return styleStack.get(param._key)[param._field];
				}
				else
				{
					return styleStack.get(param._key, null);
				}
			}
			else if (param is WorldParameter)
			{
				var paramClass : Class = WorldParameter(param)._class;

				if (param._index != -1)
				{
					return world[paramClass].getItem(param._index)[param._field];
				}
				else if (param._field != null)
				{
					return world[paramClass][param._field];
				}
				else
				{
					return world[paramClass];
				}
			}
			else if (param is TransformParameter)
			{
				return local[param._key];
			}
			else
				throw new Error('Unknown parameter type');
		}

		private function loadParameterData(paramAlloc	: ParameterAllocation,
										   constData	: Vector.<Number>,
										   data			: Object) : void
		{
			var offset	: uint	= paramAlloc._offset;
			var size	: uint	= paramAlloc._parameter._size;

			if (data is int || data is uint)
			{
				var intData : int = data as int;

				if (size == 1)
				{
					constData[offset] = intData;
				}
				else if (size == 2)
				{
					constData[offset] = ((intData & 0xFFFF0000) >>> 16) / Number(0xffff);
					constData[int(offset + 2)] = (intData & 0x0000FFFF) / Number(0xffff);
				}
				else if (size == 3)
				{
					constData[offset] = ((intData & 0xFF0000) >>> 16) / 255.;
					constData[int(offset + 1)] = ((intData & 0x00FF00) >>> 8) / 255.;
					constData[int(offset + 2)] = (intData & 0x0000FF) / 255.;
				}
				else if (size == 4)
				{
					constData[offset] = ((intData & 0xFF000000) >>> 24) / 255.;
					constData[int(offset + 1)] = ((intData & 0x00FF0000) >>> 16) / 255.;
					constData[int(offset + 2)] = ((intData & 0x0000FF00) >>> 8) / 255.;
					constData[int(offset + 3)] = ((intData & 0x000000FF)) / 255.;
				}
			}
			else if (data is Number)
			{
				if (size != 1)
					throw new Error('Parameter ' + paramAlloc.toString() + ' is ' +
						'defined as size=' + size + ' but only a Number was found');

				constData[offset] = data as Number;
			}
			else if (data is Vector4)
			{
				var vectorData : Vector4;
				vectorData = data as Vector4;

				constData[offset] = vectorData.x;
				size >= 2 && (constData[int(offset + 1)] = vectorData.y);
				size >= 3 && (constData[int(offset + 2)] = vectorData.z);
				size >= 4 && (constData[int(offset + 3)] = vectorData.w);
			}
			else if (data is Matrix4x4)
			{
				(data as Matrix4x4).getRawData(constData, offset, true);
			}
			else if (data == null)
			{
				throw new Error('Parameter ' + paramAlloc.toString() + ' is ' +
					'null and required by automatic shader');
			}
			else
			{
				throw new Error('Parameter ' + paramAlloc.toString() + ' is ' +
					'neither a int, a Number, a Vector4 or a Matrix4x4. Unable to ' +
					'map it to a shader constant.');
			}
		}

	}
}

package aerys.minko.render.shader
{
	import aerys.minko.ns.minko;
	import aerys.minko.render.renderer.state.RenderState;
	import aerys.minko.render.ressource.TextureRessource;
	import aerys.minko.render.shader.compiler.DebugCompiler;
	import aerys.minko.render.shader.compiler.allocator.ParameterAllocation;
	import aerys.minko.render.shader.node.INode;
	import aerys.minko.render.shader.node.leaf.AbstractParameter;
	import aerys.minko.render.shader.node.leaf.StyleParameter;
	import aerys.minko.render.shader.node.leaf.TransformParameter;
	import aerys.minko.render.shader.node.leaf.WorldParameter;
	import aerys.minko.scene.visitor.data.LocalData;
	import aerys.minko.scene.visitor.data.StyleStack;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Vector4;
	import aerys.minko.type.vertex.format.VertexComponent;

	import flash.utils.ByteArray;
	import flash.utils.Dictionary;

	public class DynamicShader extends Shader
	{
		use namespace minko;

		protected var _vsConstData	: Vector.<Number>;
		protected var _fsConstData	: Vector.<Number>;
		protected var _vsParams		: Vector.<ParameterAllocation>;
		protected var _fsParams		: Vector.<ParameterAllocation>;
		protected var _samplers		: Vector.<String>;

		public static function create(clipspacePosition	: INode,
									  color				: INode) : DynamicShader
		{
			var compiler : DebugCompiler = new DebugCompiler();

			compiler.load(clipspacePosition, color);

			return compiler.compileShader();
		}

		public function DynamicShader(vertexShader					: ByteArray,
									  fragmentShader				: ByteArray,
									  vertexInput					: Vector.<VertexComponent>,
									  vertexShaderConstantData		: Vector.<Number>,
									  fragmentShaderConstantData	: Vector.<Number>,
									  vertexShaderParameters		: Vector.<ParameterAllocation>,
									  fragmentShaderParameters		: Vector.<ParameterAllocation>,
									  samplers						: Vector.<String>)
		{
			_vsConstData	= vertexShaderConstantData;
			_fsConstData	= fragmentShaderConstantData;
			_vsParams		= vertexShaderParameters;
			_fsParams		= fragmentShaderParameters;
			_samplers		= samplers;

			super(vertexShader, fragmentShader, vertexInput);
		}

		public function fillRenderState(state	: RenderState,
										style	: StyleStack,
										local	: LocalData,
										world	: Dictionary) : Boolean
		{
			setTextures(state, style, local, world);
			setConstants(state, style, local, world);

			state.shader = this;

			return true;
		}

		protected function setTextures(state		: RenderState,
									   styleStack	: StyleStack,
								   	   localData	: LocalData,
									   worldData	: Object) : void
		{
			var texture 			: TextureRessource	= null;
			var samplerStyleName 	: String			= null;
			var samplerCount 		: uint 				= _samplers.length;

			for (var i : int = 0; i < samplerCount; ++i)
			{
				samplerStyleName = _samplers[i];
				texture = styleStack.get(samplerStyleName) as TextureRessource;
				state.setTextureAt(i, texture);
			}
		}

		protected function setConstants(state		: RenderState,
									    styleStack	: StyleStack,
										local		: LocalData,
										world		: Dictionary) : void
		{
			updateConstData(_vsConstData, _vsParams, styleStack, local, world);
			updateConstData(_fsConstData, _fsParams, styleStack, local, world);

			state.setVertexConstants(0, _vsConstData);
			state.setFragmentConstants(0, _fsConstData);
		}

		protected function updateConstData(constData	: Vector.<Number>,
										   paramsAllocs	: Vector.<ParameterAllocation>,
										   styleStack	: StyleStack,
										   local		: LocalData,
										   world		: Dictionary) : void
		{
			var paramLength	: int = paramsAllocs.length;

			for (var i : int = 0; i < paramLength; ++i)
			{
				var paramAlloc	: ParameterAllocation	= paramsAllocs[i];
				var param		: AbstractParameter		= paramAlloc._parameter;

				var data : Object = getParameterData(param, styleStack, local, world);

				loadParameterData(paramAlloc, constData, data);
			}
		}

		private function getParameterData(param			: AbstractParameter,
										  styleStack	: StyleStack,
										  local			: LocalData,
										  world			: Dictionary) : Object
		{
			if (param is StyleParameter)
			{
				if (param._index != -1)
				{
					return styleStack.get(param._key).getItem(param._index)[param._field];
				}
				else if (param._field != null)
				{
					return styleStack.get(param._key)[param._field];
				}
				else
				{
					return styleStack.get(param._key, null);
				}
			}
			else if (param is WorldParameter)
			{
				var paramClass : Class = WorldParameter(param)._class;

				if (param._index != -1)
				{
					return world[paramClass].getItem(param._index)[param._field];
				}
				else if (param._field != null)
				{
					return world[paramClass][param._field];
				}
				else
				{
					return world[paramClass];
				}
			}
			else if (param is TransformParameter)
			{
				return local[param._key];
			}
			else
				throw new Error('Unknown parameter type');
		}

		private function loadParameterData(paramAlloc	: ParameterAllocation,
										   constData	: Vector.<Number>,
										   data			: Object) : void
		{
			var offset	: uint	= paramAlloc._offset;
			var size	: uint	= paramAlloc._parameter._size;

			if (data is int)
			{
				var intData : int = data as int;

				if (size == 1)
				{
					constData[offset] = intData;
				}
				else if (size == 2)
				{
					constData[offset] = ((intData & 0xFFFF0000) >>> 16) / Number(0xffff);
					constData[int(offset + 2)] = (intData & 0x0000FFFF) / Number(0xffff);
				}
				else if (size == 3)
				{
					constData[offset] = ((intData & 0xFF0000) >>> 16) / 255.;
					constData[int(offset + 1)] = ((intData & 0x00FF00) >>> 8) / 255.;
					constData[int(offset + 2)] = (intData & 0x0000FF) / 255.;
				}
				else if (size == 4)
				{

					constData[offset] = ((intData & 0xFF000000) >>> 24) / 255.;
					constData[int(offset + 1)] = ((intData & 0x00FF0000) >>> 16) / 255.;
					constData[int(offset + 2)] = ((intData & 0x0000FF00) >>> 8) / 255.;
					constData[int(offset + 3)] = ((intData & 0x000000FF)) / 255.;
				}
			}
			else if (data is uint)
			{
				var uintData : uint = data as uint;

				if (size == 1)
				{
					constData[offset] = uintData;
				}
				else if (size == 2)
				{
					constData[offset] = ((uintData & 0xFFFF0000) >>> 16) / Number(0xffff);
					constData[int(offset + 2)] = (uintData & 0x0000FFFF) / Number(0xffff);
				}
				else if (size == 3)
				{
					constData[offset] = ((uintData & 0xFF0000) >>> 16) / 255.;
					constData[int(offset + 1)] = ((uintData & 0x00FF00) >>> 8) / 255.;
					constData[int(offset + 2)] = (uintData & 0x0000FF) / 255.;
				}
				else if (size == 4)
				{

					constData[offset] = ((uintData & 0xFF000000) >>> 24) / 255.;
					constData[int(offset + 1)] = ((uintData & 0x00FF0000) >>> 16) / 255.;
					constData[int(offset + 2)] = ((uintData & 0x0000FF00) >>> 8) / 255.;
					constData[int(offset + 3)] = ((uintData & 0x000000FF)) / 255.;
				}
			}
			else if (data is Number)
			{
				if (size != 1)
					throw new Error('Parameter ' + paramAlloc.toString() + ' is ' +
						'defined as size=' + size + ' but only a Number was found');

				constData[offset] = data as Number;
			}
			else if (data is Vector4)
			{
				var vectorData : Vector4;
				vectorData = data as Vector4;

				constData[offset] = vectorData.x;
				size >= 2 && (constData[int(offset + 1)] = vectorData.y);
				size >= 3 && (constData[int(offset + 2)] = vectorData.z);
				size >= 4 && (constData[int(offset + 3)] = vectorData.w);
			}
			else if (data is Matrix4x4)
			{
				(data as Matrix4x4).getRawData(constData, offset, true);
			}
			else if (data == null)
			{
				throw new Error('Parameter ' + paramAlloc.toString() + ' is ' +
					'null and required by automatic shader');
			}
			else
			{
				throw new Error('Parameter ' + paramAlloc.toString() + ' is ' +
					'neither a int, a Number, a Vector4 or a Matrix4x4. Unable to ' +
					'map it to a shader constant.');
			}
		}

	}
}

Fix bug when using uints in style to represent RGBA color


package aerys.minko.scene.node.mesh
{
	import aerys.minko.ns.minko_stream;
	import aerys.minko.scene.action.mesh.MeshAction;
	import aerys.minko.scene.action.mesh.PopMeshSkinAction;
	import aerys.minko.scene.action.mesh.PushMeshSkinAction;
	import aerys.minko.scene.node.AbstractScene;
	import aerys.minko.scene.node.group.IGroup;
	import aerys.minko.type.math.Matrix3D;
	import aerys.minko.type.stream.IVertexStream;
	import aerys.minko.type.stream.IndexStream;
	import aerys.minko.type.stream.format.VertexComponent;
	import aerys.minko.type.stream.format.VertexFormat;

	use namespace minko_stream;

	public class SkinnedMesh extends AbstractScene implements IMesh
	{
		private var _name					: String;

		private var _mesh					: IMesh;
		private var _skeletonRootName		: String;
		private var _skeletonReference		: IGroup;
		private var _bindShapeMatrix		: Matrix3D;
		private var _jointNames				: Vector.<String>;
		private var _inverseBindMatrices	: Vector.<Matrix3D>;
		private var _maxInfluences			: uint;

		public function set skeletonReference(v : IGroup) : void { _skeletonReference = v; }

		/**
		 * IMesh implementation
		 */
		public function get version()				: uint					{ return _mesh.version; }
		public function get vertexStream()			: IVertexStream			{ return _mesh.vertexStream; }
		public function get indexStream()			: IndexStream			{ return _mesh.indexStream; }

		public function get maxInfluences()			: uint					{ return _maxInfluences; }
		public function get mesh()					: IMesh					{ return _mesh; }
		public function get skeletonRootName()		: String				{ return _skeletonRootName; }
		public function get skeletonReference()		: IGroup				{ return _skeletonReference; }
		public function get bindShapeMatrix()		: Matrix3D				{ return _bindShapeMatrix; }

		public function get jointNames()			: Vector.<String>		{ return _jointNames; }
		public function get inverseBindMatrices()	: Vector.<Matrix3D>	{ return _inverseBindMatrices; }

		public function SkinnedMesh(mesh				: Mesh,
									skeletonReference	: IGroup,
									skeletonRootName	: String,
									bindShapeMatrix		: Matrix3D,
									jointNames			: Vector.<String>,
									inverseBindMatrices	: Vector.<Matrix3D>)
		{
			super();

			_name					= 'SkinnedMesh';

			_skeletonReference		= skeletonReference;
			_skeletonRootName		= skeletonRootName;
			_mesh					= mesh;
			_bindShapeMatrix		= bindShapeMatrix;
			_jointNames				= jointNames;
			_inverseBindMatrices	= inverseBindMatrices;

			_maxInfluences			= getMaxInfluencesFromVertexFormat(_mesh.vertexStream.format);

			actions.push(PushMeshSkinAction.pushMeshSkinAction,
						 MeshAction.meshAction,
						 PopMeshSkinAction.popMeshSkinAction);
		}

		private function getMaxInfluencesFromVertexFormat(vertexFormat : VertexFormat) : uint
		{
			var maxInfluences	: uint = VertexComponent.BONES.length;
			for (var i : uint = 0; i < maxInfluences; ++i)
				if (!vertexFormat.hasComponent(VertexComponent.BONES[i]))
					break;
			return i;
		}

	}
}

package aerys.minko.scene.node.mesh
{
	import aerys.minko.ns.minko_stream;
	import aerys.minko.scene.action.mesh.MeshAction;
	import aerys.minko.scene.action.mesh.PopMeshSkinAction;
	import aerys.minko.scene.action.mesh.PushMeshSkinAction;
	import aerys.minko.scene.node.AbstractScene;
	import aerys.minko.scene.node.group.IGroup;
	import aerys.minko.type.math.Matrix3D;
	import aerys.minko.type.stream.IVertexStream;
	import aerys.minko.type.stream.IndexStream;
	import aerys.minko.type.stream.format.VertexComponent;
	import aerys.minko.type.stream.format.VertexFormat;

	use namespace minko_stream;

	public class SkinnedMesh extends AbstractScene implements IMesh
	{
		private var _name					: String;

		private var _mesh					: IMesh;
		private var _skeletonRootName		: String;
		private var _skeletonReference		: IGroup;
		private var _bindShapeMatrix		: Matrix3D;
		private var _jointNames				: Vector.<String>;
		private var _inverseBindMatrices	: Vector.<Matrix3D>;
		private var _maxInfluences			: uint;

		public function set skeletonReference(v : IGroup) : void { _skeletonReference = v; }

		/**
		 * IMesh implementation
		 */
		public function get version()				: uint					{ return _mesh.version; }
		public function get vertexStream()			: IVertexStream			{ return _mesh.vertexStream; }
		public function get indexStream()			: IndexStream			{ return _mesh.indexStream; }

		public function get maxInfluences()			: uint					{ return _maxInfluences; }
		public function get mesh()					: IMesh					{ return _mesh; }
		public function get skeletonRootName()		: String				{ return _skeletonRootName; }
		public function get skeletonReference()		: IGroup				{ return _skeletonReference; }
		public function get bindShapeMatrix()		: Matrix3D				{ return _bindShapeMatrix; }

		public function get jointNames()			: Vector.<String>		{ return _jointNames; }
		public function get inverseBindMatrices()	: Vector.<Matrix3D>	{ return _inverseBindMatrices; }

		public function SkinnedMesh(mesh				: IMesh,
									skeletonReference	: IGroup,
									skeletonRootName	: String,
									bindShapeMatrix		: Matrix3D,
									jointNames			: Vector.<String>,
									inverseBindMatrices	: Vector.<Matrix3D>)
		{
			super();

			_name					= 'SkinnedMesh';

			_skeletonReference		= skeletonReference;
			_skeletonRootName		= skeletonRootName;
			_mesh					= mesh;
			_bindShapeMatrix		= bindShapeMatrix;
			_jointNames				= jointNames;
			_inverseBindMatrices	= inverseBindMatrices;

			_maxInfluences			= getMaxInfluencesFromVertexFormat(_mesh.vertexStream.format);

			actions.push(PushMeshSkinAction.pushMeshSkinAction,
						 MeshAction.meshAction,
						 PopMeshSkinAction.popMeshSkinAction);
		}

		private function getMaxInfluencesFromVertexFormat(vertexFormat : VertexFormat) : uint
		{
			var maxInfluences	: uint = VertexComponent.BONES.length;
			for (var i : uint = 0; i < maxInfluences; ++i)
				if (!vertexFormat.hasComponent(VertexComponent.BONES[i]))
					break;
			return i;
		}

	}
}

Change constructor of SkinnedMesh to allow skinning keyframedmesh


/*
 * FLINT PARTICLE SYSTEM
 * .....................
 *
 * Author: Richard Lord
 * Copyright (c) Richard Lord 2008-2010
 * http://flintparticles.org
 *
 *
 * Licence Agreement
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.flintparticles.threeD.particles
{
	import org.flintparticles.common.particles.Particle;
	import org.flintparticles.common.particles.ParticleFactory;
	import org.flintparticles.threeD.geom.Quaternion;
	import org.flintparticles.threeD.geom.Vector3DUtils;

	import flash.geom.Vector3D;

	/**
	 * The Particle3D class extends the Particle class to include state properties
	 * that are relevant to particles in 3D space.
	 */
	public class Particle3D extends Particle
	{
		/**
		 * The position of the particle (in the renderer's units).
		 */
		public var position:Vector3D;
		/**
		 * The velocity of the particle (in the renderer's units per second).
		 */
		public var velocity:Vector3D;

		/**
		 * The rotation of the particle, represented as a unit quaternion.
		 */
		public var rotation:Quaternion;

		/**
		 * The rate of rotation of the particle, represented as a vector in the direction of
		 * the axis of rotation and whose magnitude indicates the number of rotations per second.
		 */
		public var angVelocity:Vector3D;

		/**
		 * The axis in the particle's own coordinate space that
		 * indicates the direction that the particle is facing.
		 */
		public var faceAxis:Vector3D;

		private var _previousMass:Number;
		private var _previousRadius:Number;
		private var _inertia:Number;

		/**
		 * The moment of inertia of the particle about its center point
		 */
		public function get inertia():Number
		{
			if( mass != _previousMass || collisionRadius != _previousRadius )
			{
				_inertia = mass * collisionRadius * collisionRadius * 0.4;
				_previousMass = mass;
				_previousRadius = collisionRadius;
			}
			return _inertia;
		}

		/**
		 * The position of the particle in the emitter's x-axis spacial sorted array
		 */
		public var sortID:int = -1;

		/**
		 * Position vector projected into screen space. Used by renderers.
		 */
		public var projectedPosition:Vector3D;

		/**
		 * z depth of particle in renderer's camera space
		 */
		public var zDepth:Number = 0;

		/**
		 * Creates a Particle3D. Alternatively particles can be reused by using an
		 * instance of the Particle3DCreator class to create them. Usually the
		 * emitter will create the particles and the user doesn't need to create
		 * them.
		 */
		public function Particle3D()
		{
			super();
			position = Vector3DUtils.getPoint( 0, 0, 0 );
			projectedPosition = Vector3DUtils.getPoint( 0, 0, 0 );
			faceAxis = Vector3DUtils.getVector( 1, 0, 0 );
			velocity = Vector3DUtils.getVector( 0, 0, 0 );
			rotation = new Quaternion( 1, 0, 0, 0 );
			angVelocity = Vector3DUtils.getVector( 0, 0, 0 );
		}

		/**
		 * Sets the particles properties to their default values.
		 */
		override public function initialize():void
		{
			super.initialize();
			Vector3DUtils.resetPoint( position, 0, 0, 0 );
			Vector3DUtils.resetPoint( projectedPosition, 0, 0, 0 );
			Vector3DUtils.resetVector( faceAxis, 1, 0, 0 );
			Vector3DUtils.resetVector( velocity, 0, 0, 0 );
			rotation.reset( 1, 0, 0, 0 );
			Vector3DUtils.resetVector( angVelocity, 0, 0, 0 );
			sortID = -1;
			zDepth = 0;
		}

		/**
		 * @inheritDoc
		 */
		override public function clone( factory:ParticleFactory = null ):Particle
		{
			var p:Particle3D;
			if( factory )
			{
				p = factory.createParticle() as Particle3D;
			}
			else
			{
				p = new Particle3D();
			}
			cloneInto( p );
			p.position = position.clone();
			p.projectedPosition = projectedPosition.clone();
			p.faceAxis = faceAxis.clone();
			p.velocity = velocity.clone();
			p.rotation = rotation.clone();
			p.angVelocity = angVelocity.clone();
			p.zDepth = zDepth;
			return p;
		}
	}
}

/*
 * FLINT PARTICLE SYSTEM
 * .....................
 *
 * Author: Richard Lord
 * Copyright (c) Richard Lord 2008-2010
 * http://flintparticles.org
 *
 *
 * Licence Agreement
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.flintparticles.threeD.particles
{
	import org.flintparticles.common.particles.Particle;
	import org.flintparticles.common.particles.ParticleFactory;
	import org.flintparticles.threeD.geom.Quaternion;

	import flash.geom.Vector3D;

	/**
	 * The Particle3D class extends the Particle class to include state properties
	 * that are relevant to particles in 3D space.
	 */
	public class Particle3D extends Particle
	{
		/**
		 * The position of the particle (in the renderer's units).
		 */
		public var position:Vector3D;
		/**
		 * The velocity of the particle (in the renderer's units per second).
		 */
		public var velocity:Vector3D;

		/**
		 * The rotation of the particle, represented as a unit quaternion.
		 */
		public var rotation:Quaternion;

		/**
		 * The rate of rotation of the particle, represented as a vector in the direction of
		 * the axis of rotation and whose magnitude indicates the number of rotations per second.
		 */
		public var angVelocity:Vector3D;

		/**
		 * The axis in the particle's own coordinate space that
		 * indicates the direction that the particle is facing.
		 */
		public var faceAxis:Vector3D;

		private var _previousMass:Number;
		private var _previousRadius:Number;
		private var _inertia:Number;

		/**
		 * The moment of inertia of the particle about its center point
		 */
		public function get inertia():Number
		{
			if( mass != _previousMass || collisionRadius != _previousRadius )
			{
				_inertia = mass * collisionRadius * collisionRadius * 0.4;
				_previousMass = mass;
				_previousRadius = collisionRadius;
			}
			return _inertia;
		}

		/**
		 * The position of the particle in the emitter's x-axis spacial sorted array
		 */
		public var sortID:int = -1;

		/**
		 * Position vector projected into screen space. Used by renderers.
		 */
		public var projectedPosition:Vector3D;

		/**
		 * z depth of particle in renderer's camera space
		 */
		public var zDepth:Number = 0;

		/**
		 * Creates a Particle3D. Alternatively particles can be reused by using an
		 * instance of the Particle3DCreator class to create them. Usually the
		 * emitter will create the particles and the user doesn't need to create
		 * them.
		 */
		public function Particle3D()
		{
			super();
			position = new Vector3D( 0, 0, 0, 1 );
			projectedPosition = new Vector3D( 0, 0, 0, 1 );
			faceAxis = new Vector3D( 1, 0, 0, 0 );
			velocity = new Vector3D( 0, 0, 0, 0 );
			rotation = new Quaternion( 1, 0, 0, 0 );
			angVelocity = new Vector3D( 0, 0, 0, 0 );
		}

		/**
		 * Sets the particles properties to their default values.
		 */
		override public function initialize():void
		{
			super.initialize();

			position.x = 0;
			position.y = 0;
			position.z = 0;

			projectedPosition.x = 0;
			projectedPosition.y = 0;
			projectedPosition.z = 0;

			faceAxis.x = 1;
			faceAxis.y = 0;
			faceAxis.z = 0;

			velocity.x = 0;
			velocity.y = 0;
			velocity.z = 0;

			rotation.w = 1;
			rotation.x = 0;
			rotation.y = 0;
			rotation.z = 0;

			angVelocity.x = 0;
			angVelocity.y = 0;
			angVelocity.z = 0;

			sortID = -1;
			zDepth = 0;
		}

		/**
		 * @inheritDoc
		 */
		override public function clone( factory:ParticleFactory = null ):Particle
		{
			var p:Particle3D;
			if( factory )
			{
				p = factory.createParticle() as Particle3D;
			}
			else
			{
				p = new Particle3D();
			}
			cloneInto( p );
			p.position = position.clone();
			p.projectedPosition = projectedPosition.clone();
			p.faceAxis = faceAxis.clone();
			p.velocity = velocity.clone();
			p.rotation = rotation.clone();
			p.angVelocity = angVelocity.clone();
			p.zDepth = zDepth;
			return p;
		}
	}
}

Optimise the initialise method of the particle class


package asx.string
{
	import asx.array.map;

	import flash.display.JointStyle;

	/**
	 * Converts a String with a word or phrase to a CamelCaseString.
	 *
	 * Words can be separated by spaces, underscores and hyphens.
	 *
	 * @param value String to camelize.
	 * @param lowerCaseFirstWord Indicates if the first word should be lowercase or camel cased.
	 *
	 * @example
	 * <listing version="3.0">
	 *	trace(camelize("I can has camel-cases"));
	 *	// ICanHasCamelCases
	 *
	 *	trace(camelize("all your camel cases", true));
	 *	// allYourCamelCases
	 *
	 *	trace(camelize("all_the_hyphens_are_goners"));
	 *	// allTheHyphensAreGoners
	 * </listing>
	 *
	 * @author drewbourne
	 */
	public function camelize(value:String, lowerCaseFirstWord:Boolean=false):String
	{
		const underscoresHyphensAndWhitespace:RegExp = /[_-\s]+/g;

		var camelized:String = map(
			value.split(underscoresHyphensAndWhitespace),
			capitalize)
			.join("");

		return lowerCaseFirstWord
			? camelized.slice(0, 1).toLowerCase() + camelized.slice(1)
			: camelized;
	}
}

package asx.string
{
	import asx.array.map;

	/**
	 * Converts a String with a word or phrase to a CamelCaseString.
	 *
	 * Words can be separated by spaces, underscores and hyphens.
	 *
	 * @param value String to camelize.
	 * @param lowerCaseFirstWord Indicates if the first word should be lowercase or camel cased.
	 *
	 * @example
	 * <listing version="3.0">
	 *	trace(camelize("I can has camel-cases"));
	 *	// ICanHasCamelCases
	 *
	 *	trace(camelize("all your camel cases", true));
	 *	// allYourCamelCases
	 *
	 *	trace(camelize("all_the_hyphens_are_goners"));
	 *	// allTheHyphensAreGoners
	 * </listing>
	 *
	 * @author drewbourne
	 */
	public function camelize(value:String, lowerCaseFirstWord:Boolean=false):String
	{
		const underscoresHyphensAndWhitespace:RegExp = /[_-\s]+/g;

		var camelized:String = map(
			value.split(underscoresHyphensAndWhitespace),
			capitalize)
			.join("");

		return lowerCaseFirstWord
			? camelized.slice(0, 1).toLowerCase() + camelized.slice(1)
			: camelized;
	}
}

remove unused import

remove unused import