package goplayer
{
  public class ConfigurationParser
  {
    public static const DEFAULT_API_URL : String
      = "http://staging.streamio.se/api"
    public static const DEFAULT_SKIN_URL : String = "goplayer-skin.swf"
    public static const DEFAULT_TRACKER_ID : String = "global"

    public static const VALID_PARAMETERS : Array = [
      "api", "tracker", "skin", "movie", "bitrate",
      "enablertmp", "enableautoplay", "enablelooping",
      "enablechrome", "enabletitle" ]

    private const result : Configuration = new Configuration

    private var parameters : Object
    private var originalParameterNames : Object

    public function ConfigurationParser
      (parameters : Object, originalParameterNames : Object)
    {
      this.parameters = parameters
      this.originalParameterNames = originalParameterNames
    }

    public function execute() : void
    {
      result.apiURL = getString("api", DEFAULT_API_URL)
      result.trackerID = getString("tracker", DEFAULT_TRACKER_ID)
      result.skinURL = getString("skin", DEFAULT_SKIN_URL)
      result.movieID = getString("movie", null)
      result.bitratePolicy = getBitratePolicy("bitrate", BitratePolicy.BEST)
      result.enableRTMP = getBoolean("enablertmp", true)
      result.enableAutoplay = getBoolean("enableautoplay", false)
      result.enableLooping = getBoolean("enablelooping", false)
      result.enableChrome = getBoolean("enablechrome", true)
      result.enableTitle = getBoolean("enabletitle", true)
    }

    public static function parse(parameters : Object) : Configuration
    {
      const normalizedParameters : Object = {}
      const originalParameterNames : Object = {}

      for (var name : String in parameters)
        if (VALID_PARAMETERS.indexOf(normalize(name)) == -1)
          reportUnknownParameter(name)
        else
          normalizedParameters[normalize(name)] = parameters[name],
            originalParameterNames[normalize(name)] = name

      const parser : ConfigurationParser = new ConfigurationParser
        (normalizedParameters, originalParameterNames)

      parser.execute()

      return parser.result
    }

    private static function normalize(name : String) : String
    { return name.toLowerCase().replace(/[^a-z]/g, "") }

    private static function reportUnknownParameter(name : String) : void
    { debug("Error: Unknown parameter: " + name) }

    // -----------------------------------------------------

    private function getString(name : String, fallback : String) : String
    { return name in parameters ? parameters[name] : fallback }

    private function getBoolean
      (name : String, fallback : Boolean) : Boolean
    {
      if (name in parameters)
        return $getBoolean(name, parameters[name], fallback)
      else
        return fallback
    }

    private function $getBoolean
      (name : String, value : String, fallback : Boolean) : Boolean
    {
      try
        { return $$getBoolean(value) }
      catch (error : Error)
        {
          reportInvalidParameter(name, value, ["true", "false"])

          return fallback
        }

      throw new Error
    }

    private function $$getBoolean(value : String) : Boolean
    {
      if (value == "true")
        return true
      else if (value == "false")
        return false
      else
        throw new Error
    }

    // -----------------------------------------------------

    private function getBitratePolicy
      (name : String, fallback : BitratePolicy) : BitratePolicy
    {
      if (name in parameters)
        return $getBitratePolicy(name, parameters[name], fallback)
      else
        return fallback
    }

    private function $getBitratePolicy
      (name : String, value : String,
       fallback : BitratePolicy) : BitratePolicy
    {
      try
        { return $$getBitratePolicy(value) }
      catch (error : Error)
        {
          reportInvalidParameter(name, value, BITRATE_POLICY_VALUES)

          return fallback
        }

      throw new Error
    }

    private const BITRATE_POLICY_VALUES : Array =
      ["<number>kbps", "min", "max", "best"]

    private function $$getBitratePolicy(value : String) : BitratePolicy
    {
      if (value == "max")
        return BitratePolicy.MAX
      else if (value == "min")
        return BitratePolicy.MIN
      else if (value == "best")
        return BitratePolicy.BEST
      else if (Bitrate.parse(value))
        return BitratePolicy.specific(Bitrate.parse(value))
      else
        throw new Error
    }

    // -----------------------------------------------------

    private function reportInvalidParameter
      (name : String, value : String, validValues : Array) : void
    {
      debug("Error: Invalid parameter: " +
            "“" + originalParameterNames[name] + "=" + value + "”; " +
            getInvalidParameterHint(validValues) + ".")
    }

    private function getInvalidParameterHint(values : Array) : String
    { return $getInvalidParameterHint(getQuotedValues(values)) }

    private function $getInvalidParameterHint(values : Array) : String
    {
      return "please use " +
        "either " + values.slice(0, -1).join(", ") + " " +
        "or " + values[values.length - 1]
    }

    private function getQuotedValues(values : Array) : Array
    {
      var result : Array = []

      for each (var value : String in values)
        result.push("“" + value + "”")

      return result
    }
  }
}

package goplayer
{
  public class ConfigurationParser
  {
    public static const DEFAULT_API_URL : String
      = "http://staging.streamio.se/api"
    public static const DEFAULT_SKIN_URL : String = "goplayer-skin.swf"
    public static const DEFAULT_TRACKER_ID : String = "global"

    public static const VALID_PARAMETERS : Array = [
      "api", "tracker", "skin", "movie", "bitrate",
      "enablertmp", "autoplay", "loop",
      "enablechrome", "enabletitle" ]

    private const result : Configuration = new Configuration

    private var parameters : Object
    private var originalParameterNames : Object

    public function ConfigurationParser
      (parameters : Object, originalParameterNames : Object)
    {
      this.parameters = parameters
      this.originalParameterNames = originalParameterNames
    }

    public function execute() : void
    {
      result.apiURL = getString("api", DEFAULT_API_URL)
      result.trackerID = getString("tracker", DEFAULT_TRACKER_ID)
      result.skinURL = getString("skin", DEFAULT_SKIN_URL)
      result.movieID = getString("movie", null)
      result.bitratePolicy = getBitratePolicy("bitrate", BitratePolicy.BEST)
      result.enableRTMP = getBoolean("enablertmp", true)
      result.enableAutoplay = getBoolean("autoplay", false)
      result.enableLooping = getBoolean("loop", false)
      result.enableChrome = getBoolean("enablechrome", true)
      result.enableTitle = getBoolean("enabletitle", true)
    }

    public static function parse(parameters : Object) : Configuration
    {
      const normalizedParameters : Object = {}
      const originalParameterNames : Object = {}

      for (var name : String in parameters)
        if (VALID_PARAMETERS.indexOf(normalize(name)) == -1)
          reportUnknownParameter(name)
        else
          normalizedParameters[normalize(name)] = parameters[name],
            originalParameterNames[normalize(name)] = name

      const parser : ConfigurationParser = new ConfigurationParser
        (normalizedParameters, originalParameterNames)

      parser.execute()

      return parser.result
    }

    private static function normalize(name : String) : String
    { return name.toLowerCase().replace(/[^a-z]/g, "") }

    private static function reportUnknownParameter(name : String) : void
    { debug("Error: Unknown parameter: " + name) }

    // -----------------------------------------------------

    private function getString(name : String, fallback : String) : String
    { return name in parameters ? parameters[name] : fallback }

    private function getBoolean
      (name : String, fallback : Boolean) : Boolean
    {
      if (name in parameters)
        return $getBoolean(name, parameters[name], fallback)
      else
        return fallback
    }

    private function $getBoolean
      (name : String, value : String, fallback : Boolean) : Boolean
    {
      try
        { return $$getBoolean(value) }
      catch (error : Error)
        {
          reportInvalidParameter(name, value, ["true", "false"])

          return fallback
        }

      throw new Error
    }

    private function $$getBoolean(value : String) : Boolean
    {
      if (value == "true")
        return true
      else if (value == "false")
        return false
      else
        throw new Error
    }

    // -----------------------------------------------------

    private function getBitratePolicy
      (name : String, fallback : BitratePolicy) : BitratePolicy
    {
      if (name in parameters)
        return $getBitratePolicy(name, parameters[name], fallback)
      else
        return fallback
    }

    private function $getBitratePolicy
      (name : String, value : String,
       fallback : BitratePolicy) : BitratePolicy
    {
      try
        { return $$getBitratePolicy(value) }
      catch (error : Error)
        {
          reportInvalidParameter(name, value, BITRATE_POLICY_VALUES)

          return fallback
        }

      throw new Error
    }

    private const BITRATE_POLICY_VALUES : Array =
      ["<number>kbps", "min", "max", "best"]

    private function $$getBitratePolicy(value : String) : BitratePolicy
    {
      if (value == "max")
        return BitratePolicy.MAX
      else if (value == "min")
        return BitratePolicy.MIN
      else if (value == "best")
        return BitratePolicy.BEST
      else if (Bitrate.parse(value))
        return BitratePolicy.specific(Bitrate.parse(value))
      else
        throw new Error
    }

    // -----------------------------------------------------

    private function reportInvalidParameter
      (name : String, value : String, validValues : Array) : void
    {
      debug("Error: Invalid parameter: " +
            "“" + originalParameterNames[name] + "=" + value + "”; " +
            getInvalidParameterHint(validValues) + ".")
    }

    private function getInvalidParameterHint(values : Array) : String
    { return $getInvalidParameterHint(getQuotedValues(values)) }

    private function $getInvalidParameterHint(values : Array) : String
    {
      return "please use " +
        "either " + values.slice(0, -1).join(", ") + " " +
        "or " + values[values.length - 1]
    }

    private function getQuotedValues(values : Array) : Array
    {
      var result : Array = []

      for each (var value : String in values)
        result.push("“" + value + "”")

      return result
    }
  }
}

Change `enable-autoplay' and `enable-looping' to `autoplay' and `loop'.


//
// Flump - Copyright 2012 Three Rings Design

package flump.export {

import flash.display.BitmapData;
import flash.filesystem.File;

import executor.Executor;
import executor.load.ImageLoader;
import executor.load.LoadedImage;

import flump.display.Movie;
import flump.xfl.XflLibrary;
import flump.xfl.XflMovie;
import flump.xfl.XflTexture;

import starling.display.DisplayObject;
import starling.display.Image;
import starling.display.Sprite;
import starling.textures.Texture;

import com.threerings.util.Map;
import com.threerings.util.Maps;

import com.threerings.display.Animation;

public class Preview extends Sprite
{
    public function displayAnimation (base :File, lib :XflLibrary, xflMovie :XflMovie) :void {
        loadTextures(base, lib, function (..._) :void {
            var movie :Movie = new Movie(xflMovie, function (symbol :String) :DisplayObject {
                const xflTex :XflTexture = _xflTextures.get(symbol);
                const packed :PackedTexture = PackedTexture.fromTexture(xflTex, lib);
                const image :Image = new Image(Texture.fromBitmapData(packed.toBitmapData()));
                image.x = packed.offset.x;
                image.y = packed.offset.y;
                const holder :Sprite = new Sprite();
                holder.addChild(image);
                return holder;
            });
            addChild(movie);
        });
    }

    public function loadTextures (base :File, lib :XflLibrary, onLoaded :Function) :void {
        var loader :Executor = new Executor();
        for each (var tex :XflTexture in lib.textures) {
            if (_textures.containsKey(tex.name)) continue;
            new ImageLoader().loadFromUrl(tex.exportPath(base).url, loader).succeeded.add(
                textureAdder(tex));
        }
        loader.terminated.add(function (..._) :void { onLoaded(); });
        loader.shutdown();
    }

    public function textureAdder (tex :XflTexture) :Function {
        return function (img :LoadedImage) :void {
            _xflTextures.put(tex.name, tex);
            _textures.put(tex.name, Texture.fromBitmap(img.bitmap));
        };
    }

    public function displayTextures (base :File, lib :XflLibrary) :void {
        loadTextures(base, lib, function (..._) :void {
            var x :int = 0;
            for each (var tex :Texture in _textures.values()) {
                var img :Image = new Image(tex);
                img.x = x;
                x += img.width;
                addChild(img);
            }
        });
    }

    protected const _xflTextures :Map = Maps.newMapOf(String);
    protected const _textures :Map = Maps.newMapOf(String);
}
}

//
// Flump - Copyright 2012 Three Rings Design

package flump.export {

import flash.display.BitmapData;
import flash.filesystem.File;

import flump.display.Movie;
import flump.xfl.XflLibrary;
import flump.xfl.XflMovie;
import flump.xfl.XflTexture;

import starling.display.DisplayObject;
import starling.display.Image;
import starling.display.Sprite;
import starling.textures.Texture;

import com.threerings.display.Animation;

public class Preview extends Sprite
{
    public function displayAnimation (base :File, lib :XflLibrary, xflMovie :XflMovie) :void {
        var movie :Movie = new Movie(xflMovie, function (symbol :String) :DisplayObject {
            const xflTex :XflTexture = lib.lookup(symbol);
            const packed :PackedTexture = PackedTexture.fromTexture(xflTex, lib);
            const image :Image = new Image(Texture.fromBitmapData(packed.toBitmapData()));
            image.x = packed.offset.x;
            image.y = packed.offset.y;
            const holder :Sprite = new Sprite();
            holder.addChild(image);
            return holder;
        });
        addChild(movie);
    }
}
}

Use XflLibrary's symbol lookup in Preview

The texture loading from disk is gone for now


// ===================================================================================================
//                           _  __     _ _
//                          | |/ /__ _| | |_ _  _ _ _ __ _
//                          | ' </ _` | |  _| || | '_/ _` |
//                          |_|\_\__,_|_|\__|\_,_|_| \__,_|
//
// This file is part of the Kaltura Collaborative Media Suite which allows users
// to do with audio, video, and animation what Wiki platfroms allow them to do with
// text.
//
// Copyright (C) 2006-2011  Kaltura Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// @ignore
// ===================================================================================================
package com.kaltura.delegates {

	import com.kaltura.KalturaClient;
	import com.kaltura.config.IKalturaConfig;
	import com.kaltura.config.KalturaConfig;
	import com.kaltura.core.KClassFactory;
	import com.kaltura.encryption.MD5;
	import com.kaltura.errors.KalturaError;
	import com.kaltura.events.KalturaEvent;
	import com.kaltura.net.KalturaCall;

	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.TimerEvent;
	import flash.net.FileReference;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.utils.Timer;
	import flash.utils.getDefinitionByName;

	public class WebDelegateBase extends EventDispatcher implements IKalturaCallDelegate {

		public static var CONNECT_TIME:int = 120000; //120 secs
		public static var LOAD_TIME:int = 120000; //120 secs

		protected var connectTimer:Timer;
		protected var loadTimer:Timer;

		protected var _call:KalturaCall;
		protected var _config:KalturaConfig;

		protected var loader:URLLoader;
		protected var fileRef:FileReference;

		//Setters & getters
		public function get call():KalturaCall {
			return _call;
		}

		public function set call(newVal:KalturaCall):void {
			_call = newVal;
		}

		public function get config():IKalturaConfig {
			return _config;
		}

		public function set config(newVal:IKalturaConfig):void {
			_config = newVal as KalturaConfig;
		}

		public function WebDelegateBase(call:KalturaCall = null, config:KalturaConfig = null) {
			this.call = call;
			this.config = config;
			if (!call)
				return; //maybe a multi request
			if (call.useTimeout) {
				connectTimer = new Timer(CONNECT_TIME, 1);
				connectTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onConnectTimeout);

				loadTimer = new Timer(LOAD_TIME, 1);
				loadTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onLoadTimeOut);
			}
			execute();
		}

		public function close():void {
			try {
				loader.close();
			}
			catch (e:*) {
			}

			if (call.useTimeout) {
				connectTimer.stop();
				loadTimer.stop();
			}
		}

		protected function onConnectTimeout(event:TimerEvent):void {
			var kError:KalturaError = new KalturaError();
			kError.errorCode = "CONNECTION_TIMEOUT";
			kError.errorMsg = "Connection Timeout: " + CONNECT_TIME / 1000 + " sec with no post command from kaltura client.";
			_call.handleError(kError);
			dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));

			loadTimer.stop();
			close();
		}

		protected function onLoadTimeOut(event:TimerEvent):void {
			connectTimer.stop();

			close();

			var kError:KalturaError = new KalturaError();
			kError.errorCode = "POST_TIMEOUT";
			kError.errorMsg = "Post Timeout: " + LOAD_TIME / 1000 + " sec with no post result.";
			_call.handleError(kError);
			dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));
		}

		protected function execute():void {
			if (call == null) {
				throw new Error('No call defined.');
			}
			post(); //post the call
		}

		/**
		* Helper function for sending the call straight to the server
		*/
		protected function post():void {

			addOptionalArguments();

			formatRequest();

			sendRequest();

			if (call.useTimeout) {
				connectTimer.start();
			}
		}

		protected function formatRequest():void {
			//The configuration is stronger then the args
			if (_config.partnerId != null && _call.args["partnerId"] == -1)
				_call.setRequestArgument("partnerId", _config.partnerId);

			if (_config.ks != null)
				_call.setRequestArgument("ks", _config.ks);

			if (_config.clientTag != null)
				_call.setRequestArgument("clientTag", _config.clientTag);

			_call.setRequestArgument("ignoreNull", _config.ignoreNull);
			_call.setRequestArgument("apiVersion", KalturaClient.API_VERSION);

			//Create signature hash.
			//call.setRequestArgument("kalsig", getMD5Checksum(call));
		}

		protected function getMD5Checksum(call:KalturaCall):String {
			var props:Array = new Array();
			for (var arg:String in call.args)
				props.push(arg);

			props.sort();

			var s:String = "";
			for each (var prop:String in props) {

				if ( call.args[prop] )
					s += prop +  call.args[prop];
			}

			return MD5.encrypt(s);
		}

		protected function sendRequest():void {

			//construct the loader
			createURLLoader();

			//Create signature hash.
			var kalsig:String = getMD5Checksum(call);

			//create the service request for normal calls
			var url:String = _config.protocol + _config.domain + "/" + _config.srvUrl + "?service=" + call.service + "&action=" + call.action + "&kalsig=" + kalsig;;

			if (_call.method == URLRequestMethod.GET)
				url += "&";

			var req:URLRequest = new URLRequest(url);
			req.contentType = "application/x-www-form-urlencoded";
			req.method = call.method;
			req.data = call.args;

			loader.dataFormat = URLLoaderDataFormat.TEXT;
			loader.load(req);
		}

		protected function createURLLoader():void {
			loader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, onDataComplete);
			loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus);
			loader.addEventListener(IOErrorEvent.IO_ERROR, onError);
			loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			loader.addEventListener(Event.OPEN, onOpen);
		}

		protected function onHTTPStatus(event:HTTPStatusEvent):void {
		}

		protected function onOpen(event:Event):void {
			if (call.useTimeout) {
				connectTimer.stop();
				loadTimer.start();
			}
		}

		protected function addOptionalArguments():void {
			//add optional args here
		}

		// Event Handlers

		/**
		 * try to process received data.
		 * if procesing failed, let call handle the processing error
		 * @param event load complete event
		 */
		protected function onDataComplete(event:Event):void {
			try {
				handleResult(XML(event.target.data));
			}
			catch (e:Error) {
				var kErr:KalturaError = new KalturaError();
				kErr.errorCode = String(e.errorID);
				kErr.errorMsg = e.message;
				_call.handleError(kErr);
			}
		}

		/**
		 * handle io or security error events from the loader.
		 * create relevant KalturaError, let the call process it.
		 * @param event
		 */
		protected function onError(event:ErrorEvent):void {
			clean();
			var kError:KalturaError = createKalturaError(event, loader.data);

			if (!kError) {
				kError = new KalturaError();
				kError.errorMsg = event.text;
				kError.errorCode = event.type; 	// either IOErrorEvent.IO_ERROR or SecurityErrorEvent.SECURITY_ERROR
			}

			call.handleError(kError);

			dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));
		}

		/**
		 * parse the server's response and let the call process it.
		 * @param result  server's response
		 */
		protected function handleResult(result:XML):void {
			clean();

			var error:KalturaError = validateKalturaResponse(result);

			if (error == null) {
				var digestedResult:Object = parse(result);
				call.handleResult(digestedResult);
			}
			else {
				call.handleError(error);
			}
		}

		/**
		* stop timers and clean event listeners
		 */
		protected function clean():void {
			if (call.useTimeout) {
				connectTimer.stop();
				loadTimer.stop();
			}

			if (loader == null) {
				return;
			}

			loader.removeEventListener(Event.COMPLETE, onDataComplete);
			loader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
			loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			loader.removeEventListener(Event.OPEN, onOpen);
		}

		/**
		* create the correct object and populate it with the given values. if the needed class is not found
		 * in the file, a generic object is created with attributes matching the XML attributes.
		* Override this parssing function in the specific delegate to create the correct object.
		* @param   result      instance attributes
		* @return an instance of the class declared by the given XML.
		* */
		public function parse(result:XML):* {
			//by defualt create the response object
			var cls:Class;
			try {
				cls = getDefinitionByName('com.kaltura.vo.' + result.result.objectType) as Class;
			}
			catch (e:Error) {
				cls = Object;
			}
			var obj:* = (new KClassFactory(cls)).newInstanceFromXML(result.result);
			return obj;
		}

		/**
		 * If the result string holds an error, return a KalturaError object with
		 * relevant values. <br/>
		 * Overide this to create validation object and fill it.
		 * @param result  the string returned from the server.
		 * @return  matching error object
		 */
		protected function validateKalturaResponse(result:String):KalturaError {
			var kError:KalturaError = null;
			var xml:XML = XML(result);
			if (xml.result.hasOwnProperty('error')
				&& xml.result.error.hasOwnProperty('code')
				&& xml.result.error.hasOwnProperty('message')) {

				kError = new KalturaError();
				kError.errorCode = String(xml.result.error.code);
				kError.errorMsg = xml.result.error.message;
				dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));
			}

			return kError;
		}

		/**
		 * create error object and fill it with relevant details
		 * @param event
		 * @param loaderData
		 * @return detailed KalturaError to be processed
		 */
		protected function createKalturaError(event:ErrorEvent, loaderData:*):KalturaError {
			var ke:KalturaError = new KalturaError();
			ke.errorMsg = event.text;
			ke.errorCode = event.type;
			return ke;
		}

		/**
		* create the url that is used for serve actions
		* @param call    the KalturaCall that defines the required parameters
		 * @return URLRequest with relevant parameters
		* */
		public function getServeUrl(call:KalturaCall):URLRequest {
			var url:String = _config.protocol + _config.domain + "/" + _config.srvUrl + "?service=" + call.service + "&action=" + call.action;
			for (var key:String in call.args) {
				url += "&" + key + "=" + call.args[key];
			}
			var req:URLRequest = new URLRequest(url);
			req.contentType = "application/x-www-form-urlencoded";
			req.method = call.method;
//          req.data = call.args;
			return req;
		}

	}
}

// ===================================================================================================
//                           _  __     _ _
//                          | |/ /__ _| | |_ _  _ _ _ __ _
//                          | ' </ _` | |  _| || | '_/ _` |
//                          |_|\_\__,_|_|\__|\_,_|_| \__,_|
//
// This file is part of the Kaltura Collaborative Media Suite which allows users
// to do with audio, video, and animation what Wiki platfroms allow them to do with
// text.
//
// Copyright (C) 2006-2011  Kaltura Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// @ignore
// ===================================================================================================
package com.kaltura.delegates {

	import com.kaltura.KalturaClient;
	import com.kaltura.config.IKalturaConfig;
	import com.kaltura.config.KalturaConfig;
	import com.kaltura.core.KClassFactory;
	import com.kaltura.encryption.MD5;
	import com.kaltura.errors.KalturaError;
	import com.kaltura.events.KalturaEvent;
	import com.kaltura.net.KalturaCall;

	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.TimerEvent;
	import flash.net.FileReference;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.utils.Timer;
	import flash.utils.getDefinitionByName;

	public class WebDelegateBase extends EventDispatcher implements IKalturaCallDelegate {

		public static var CONNECT_TIME:int = 120000; //120 secs
		public static var LOAD_TIME:int = 120000; //120 secs

		protected var connectTimer:Timer;
		protected var loadTimer:Timer;

		protected var _call:KalturaCall;
		protected var _config:KalturaConfig;

		protected var loader:URLLoader;
		protected var fileRef:FileReference;

		//Setters & getters
		public function get call():KalturaCall {
			return _call;
		}

		public function set call(newVal:KalturaCall):void {
			_call = newVal;
		}

		public function get config():IKalturaConfig {
			return _config;
		}

		public function set config(newVal:IKalturaConfig):void {
			_config = newVal as KalturaConfig;
		}

		public function WebDelegateBase(call:KalturaCall = null, config:KalturaConfig = null) {
			this.call = call;
			this.config = config;
			if (!call)
				return; //maybe a multi request
			if (call.useTimeout) {
				connectTimer = new Timer(CONNECT_TIME, 1);
				connectTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onConnectTimeout);

				loadTimer = new Timer(LOAD_TIME, 1);
				loadTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onLoadTimeOut);
			}
			execute();
		}

		public function close():void {
			try {
				loader.close();
			}
			catch (e:*) {
			}

			if (call.useTimeout) {
				connectTimer.stop();
				loadTimer.stop();
			}
		}

		protected function onConnectTimeout(event:TimerEvent):void {
			var kError:KalturaError = new KalturaError();
			kError.errorCode = "CONNECTION_TIMEOUT";
			kError.errorMsg = "Connection Timeout: " + CONNECT_TIME / 1000 + " sec with no post command from kaltura client.";
			_call.handleError(kError);
			dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));

			loadTimer.stop();
			close();
		}

		protected function onLoadTimeOut(event:TimerEvent):void {
			connectTimer.stop();

			close();

			var kError:KalturaError = new KalturaError();
			kError.errorCode = "POST_TIMEOUT";
			kError.errorMsg = "Post Timeout: " + LOAD_TIME / 1000 + " sec with no post result.";
			_call.handleError(kError);
			dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));
		}

		protected function execute():void {
			if (call == null) {
				throw new Error('No call defined.');
			}
			post(); //post the call
		}

		/**
		* Helper function for sending the call straight to the server
		*/
		protected function post():void {

			addOptionalArguments();

			formatRequest();

			sendRequest();

			if (call.useTimeout) {
				connectTimer.start();
			}
		}

		protected function formatRequest():void {
			//The configuration is stronger then the args
			if (_config.partnerId != null && (!_call.args["partnerId"] || _call.args["partnerId"]==-1))
				_call.setRequestArgument("partnerId", _config.partnerId);

			if (_config.ks != null)
				_call.setRequestArgument("ks", _config.ks);

			if (_config.clientTag != null)
				_call.setRequestArgument("clientTag", _config.clientTag);

			_call.setRequestArgument("ignoreNull", _config.ignoreNull);
			_call.setRequestArgument("apiVersion", KalturaClient.API_VERSION);

			//Create signature hash.
			//call.setRequestArgument("kalsig", getMD5Checksum(call));
		}

		protected function getMD5Checksum(call:KalturaCall):String {
			var props:Array = new Array();
			for (var arg:String in call.args)
				props.push(arg);

			props.sort();

			var s:String = "";
			for each (var prop:String in props) {

				if ( call.args[prop] )
					s += prop +  call.args[prop];
			}

			return MD5.encrypt(s);
		}

		protected function sendRequest():void {

			//construct the loader
			createURLLoader();

			//Create signature hash.
			var kalsig:String = getMD5Checksum(call);

			//create the service request for normal calls
			var url:String = _config.protocol + _config.domain + "/" + _config.srvUrl + "?service=" + call.service + "&action=" + call.action + "&kalsig=" + kalsig;;

			if (_call.method == URLRequestMethod.GET)
				url += "&";

			var req:URLRequest = new URLRequest(url);
			req.contentType = "application/x-www-form-urlencoded";
			req.method = call.method;
			req.data = call.args;

			loader.dataFormat = URLLoaderDataFormat.TEXT;
			loader.load(req);
		}

		protected function createURLLoader():void {
			loader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, onDataComplete);
			loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus);
			loader.addEventListener(IOErrorEvent.IO_ERROR, onError);
			loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			loader.addEventListener(Event.OPEN, onOpen);
		}

		protected function onHTTPStatus(event:HTTPStatusEvent):void {
		}

		protected function onOpen(event:Event):void {
			if (call.useTimeout) {
				connectTimer.stop();
				loadTimer.start();
			}
		}

		protected function addOptionalArguments():void {
			//add optional args here
		}

		// Event Handlers

		/**
		 * try to process received data.
		 * if procesing failed, let call handle the processing error
		 * @param event load complete event
		 */
		protected function onDataComplete(event:Event):void {
			try {
				handleResult(XML(event.target.data));
			}
			catch (e:Error) {
				var kErr:KalturaError = new KalturaError();
				kErr.errorCode = String(e.errorID);
				kErr.errorMsg = e.message;
				_call.handleError(kErr);
			}
		}

		/**
		 * handle io or security error events from the loader.
		 * create relevant KalturaError, let the call process it.
		 * @param event
		 */
		protected function onError(event:ErrorEvent):void {
			clean();
			var kError:KalturaError = createKalturaError(event, loader.data);

			if (!kError) {
				kError = new KalturaError();
				kError.errorMsg = event.text;
				kError.errorCode = event.type; 	// either IOErrorEvent.IO_ERROR or SecurityErrorEvent.SECURITY_ERROR
			}

			call.handleError(kError);

			dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));
		}

		/**
		 * parse the server's response and let the call process it.
		 * @param result  server's response
		 */
		protected function handleResult(result:XML):void {
			clean();

			var error:KalturaError = validateKalturaResponse(result);

			if (error == null) {
				var digestedResult:Object = parse(result);
				call.handleResult(digestedResult);
			}
			else {
				call.handleError(error);
			}
		}

		/**
		* stop timers and clean event listeners
		 */
		protected function clean():void {
			if (call.useTimeout) {
				connectTimer.stop();
				loadTimer.stop();
			}

			if (loader == null) {
				return;
			}

			loader.removeEventListener(Event.COMPLETE, onDataComplete);
			loader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
			loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			loader.removeEventListener(Event.OPEN, onOpen);
		}

		/**
		* create the correct object and populate it with the given values. if the needed class is not found
		 * in the file, a generic object is created with attributes matching the XML attributes.
		* Override this parssing function in the specific delegate to create the correct object.
		* @param   result      instance attributes
		* @return an instance of the class declared by the given XML.
		* */
		public function parse(result:XML):* {
			//by defualt create the response object
			var cls:Class;
			try {
				cls = getDefinitionByName('com.kaltura.vo.' + result.result.objectType) as Class;
			}
			catch (e:Error) {
				cls = Object;
			}
			var obj:* = (new KClassFactory(cls)).newInstanceFromXML(result.result);
			return obj;
		}

		/**
		 * If the result string holds an error, return a KalturaError object with
		 * relevant values. <br/>
		 * Overide this to create validation object and fill it.
		 * @param result  the string returned from the server.
		 * @return  matching error object
		 */
		protected function validateKalturaResponse(result:String):KalturaError {
			var kError:KalturaError = null;
			var xml:XML = XML(result);
			if (xml.result.hasOwnProperty('error')
				&& xml.result.error.hasOwnProperty('code')
				&& xml.result.error.hasOwnProperty('message')) {

				kError = new KalturaError();
				kError.errorCode = String(xml.result.error.code);
				kError.errorMsg = xml.result.error.message;
				dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));
			}

			return kError;
		}

		/**
		 * create error object and fill it with relevant details
		 * @param event
		 * @param loaderData
		 * @return detailed KalturaError to be processed
		 */
		protected function createKalturaError(event:ErrorEvent, loaderData:*):KalturaError {
			var ke:KalturaError = new KalturaError();
			ke.errorMsg = event.text;
			ke.errorCode = event.type;
			return ke;
		}

		/**
		* create the url that is used for serve actions
		* @param call    the KalturaCall that defines the required parameters
		 * @return URLRequest with relevant parameters
		* */
		public function getServeUrl(call:KalturaCall):URLRequest {
			var url:String = _config.protocol + _config.domain + "/" + _config.srvUrl + "?service=" + call.service + "&action=" + call.action;
			for (var key:String in call.args) {
				url += "&" + key + "=" + call.args[key];
			}
			var req:URLRequest = new URLRequest(url);
			req.contentType = "application/x-www-form-urlencoded";
			req.method = call.method;
//          req.data = call.args;
			return req;
		}

	}
}

fix client code to send partnerId from client config by default


package
{
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.net.Socket;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.utils.ByteArray;

    public class swfcat extends Sprite
    {
        private var output_text:TextField;

        private function puts(s:String):void
        {
            output_text.appendText(s + "\n");
        }

        public function swfcat()
        {
            output_text = new TextField();
            output_text.width = 400;
            output_text.height = 300;
            output_text.background = true;
            output_text.backgroundColor = 0x001f0f;
            output_text.textColor = 0x44CC44;
            addChild(output_text);

            var s1:Socket = new Socket();
            var s2:Socket = new Socket();

            s1.addEventListener(Event.CONNECT, function (e:Event):void {
                puts("s1 Connected.");
            });
            s1.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("s1 Closed.");
            });
            s1.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                puts("s1 IO error: " + e.text + ".");
            });
            s1.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                puts("s1 Security error: " + e.text + ".");
            });
            s1.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
                var bytes:ByteArray;
                puts("s1 progress: " + e.bytesLoaded + ".");
                s1.readBytes(bytes, 0, e.bytesLoaded);
                s2.writeBytes(bytes);
            });

            s2.addEventListener(Event.CONNECT, function (e:Event):void {
                puts("s2 Connected.");
            });
            s2.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("s2 Closed.");
            });
            s2.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                puts("s2 IO error: " + e.text + ".");
            });
            s2.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                puts("s2 Security error: " + e.text + ".");
            });
            s2.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
                var bytes:ByteArray;
                puts("s2 progress: " + e.bytesLoaded + ".");
                s2.readBytes(bytes, 0, e.bytesLoaded);
                s1.writeBytes(bytes);
            });

            puts("Requesting connection.");

            s1.connect("10.32.16.133", 9998);
            s2.connect("10.32.16.133", 9999);

            puts("Connection requested.");
        }
    }
}

package
{
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.net.Socket;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.utils.ByteArray;

    public class swfcat extends Sprite
    {
        private var output_text:TextField;

        private function puts(s:String):void
        {
            output_text.appendText(s + "\n");
        }

        public function swfcat()
        {
            output_text = new TextField();
            output_text.width = 400;
            output_text.height = 300;
            output_text.background = true;
            output_text.backgroundColor = 0x001f0f;
            output_text.textColor = 0x44CC44;
            addChild(output_text);

            var s1:Socket = new Socket();
            var s2:Socket = new Socket();

            s1.addEventListener(Event.CONNECT, function (e:Event):void {
                puts("s1 Connected.");
            });
            s1.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("s1 Closed.");
            });
            s1.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                puts("s1 IO error: " + e.text + ".");
            });
            s1.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                puts("s1 Security error: " + e.text + ".");
            });
            s1.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
                var bytes:ByteArray = new ByteArray();
                puts("s1 progress: " + e.bytesLoaded + ".");
                s1.readBytes(bytes, 0, e.bytesLoaded);
                puts("s1 bytes length: " + bytes.length + ".");
                s2.writeBytes(bytes);
            });

            s2.addEventListener(Event.CONNECT, function (e:Event):void {
                puts("s2 Connected.");
            });
            s2.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("s2 Closed.");
            });
            s2.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                puts("s2 IO error: " + e.text + ".");
            });
            s2.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                puts("s2 Security error: " + e.text + ".");
            });
            s2.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
                var bytes:ByteArray = new ByteArray();
                puts("s2 progress: " + e.bytesLoaded + ".");
                s2.readBytes(bytes, 0, e.bytesLoaded);
                puts("s2 bytes length: " + bytes.length + ".");
                s1.writeBytes(bytes);
            });

            puts("Requesting connection.");

            s1.connect("10.32.16.133", 9998);
            s2.connect("10.32.16.133", 9999);

            puts("Connection requested.");
        }
    }
}

Fix the last commit; we must actually instantiate the ByteArrays.


package
{
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.net.Socket;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.utils.ByteArray;

    public class swfcat extends Sprite
    {
        private var output_text:TextField;

        private function puts(s:String):void
        {
            output_text.appendText(s + "\n");
        }

        public function swfcat()
        {
            output_text = new TextField();
            output_text.width = 400;
            output_text.height = 300;
            output_text.background = true;
            output_text.backgroundColor = 0x001f0f;
            output_text.textColor = 0x44CC44;
            addChild(output_text);

            var s1:Socket = new Socket();
            var s2:Socket = new Socket();

            s1.addEventListener(Event.CONNECT, function (e:Event):void {
                puts("s1 Connected.");
            });
            s1.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("s1 Closed.");
            });
            s1.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                puts("s1 IO error: " + e.text + ".");
            });
            s1.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                puts("s1 Security error: " + e.text + ".");
            });
            s1.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
                var bytes:ByteArray = new ByteArray();
                puts("s1 progress: " + e.bytesLoaded + ".");
                s1.readBytes(bytes, 0, e.bytesLoaded);
                puts("s1 bytes length: " + bytes.length + ".");
                s2.writeBytes(bytes);
            });

            s2.addEventListener(Event.CONNECT, function (e:Event):void {
                puts("s2 Connected.");
            });
            s2.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("s2 Closed.");
            });
            s2.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                puts("s2 IO error: " + e.text + ".");
            });
            s2.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                puts("s2 Security error: " + e.text + ".");
            });
            s2.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
                var bytes:ByteArray = new ByteArray();
                puts("s2 progress: " + e.bytesLoaded + ".");
                s2.readBytes(bytes, 0, e.bytesLoaded);
                puts("s2 bytes length: " + bytes.length + ".");
                s1.writeBytes(bytes);
            });

            puts("Requesting connection.");

            s1.connect("10.32.16.133", 9998);
            s2.connect("10.32.16.133", 9999);

            puts("Connection requested.");
        }
    }
}

package
{
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.net.Socket;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.utils.ByteArray;

    public class swfcat extends Sprite
    {
        private const TOR_ADDRESS:String = "upload.bamsoftware.com";
        private const TOR_PORT:int = 9001;
        private const CLIENT_ADDRESS:String = "192.168.0.2";
        private const CLIENT_PORT:int = 9001;

        private var output_text:TextField;

        private function puts(s:String):void
        {
            output_text.appendText(s + "\n");
        }

        public function swfcat()
        {
            output_text = new TextField();
            output_text.width = 400;
            output_text.height = 300;
            output_text.background = true;
            output_text.backgroundColor = 0x001f0f;
            output_text.textColor = 0x44CC44;
            addChild(output_text);

            var s_t:Socket = new Socket();
            var s_c:Socket = new Socket();

            s_t.addEventListener(Event.CONNECT, function (e:Event):void {
                puts("Tor: connected.");
            });
            s_t.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("Tor: closed.");
            });
            s_t.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                puts("Tor: I/O error: " + e.text + ".");
            });
            s_t.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                puts("Tor: security error: " + e.text + ".");
            });
            s_t.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
                var bytes:ByteArray = new ByteArray();
                s_t.readBytes(bytes, 0, e.bytesLoaded);
                puts("Tor: read " + bytes.length + ".");
                s_c.writeBytes(bytes);
            });

            s_c.addEventListener(Event.CONNECT, function (e:Event):void {
                puts("Client: connected.");
            });
            s_c.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("Client: closed.");
            });
            s_c.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                puts("Client: I/O error: " + e.text + ".");
            });
            s_c.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                puts("Client: security error: " + e.text + ".");
            });
            s_c.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
                var bytes:ByteArray = new ByteArray();
                s_c.readBytes(bytes, 0, e.bytesLoaded);
                puts("Client: read " + bytes.length + ".");
                s_t.writeBytes(bytes);
            });

            puts("Tor: connecting.");
            s_t.connect(TOR_ADDRESS, TOR_PORT);
            puts("Client: connecting.");
            s_c.connect(CLIENT_ADDRESS, CLIENT_PORT);
        }
    }
}

Change socket variable names and point them at a real Tor relay and a hardcoded client.


package aerys.minko.render.geometry.primitive
{
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.geometry.stream.IVertexStream;
	import aerys.minko.render.geometry.stream.IndexStream;
	import aerys.minko.render.geometry.stream.StreamUsage;
	import aerys.minko.render.geometry.stream.VertexStream;
	import aerys.minko.render.geometry.stream.format.VertexComponent;
	import aerys.minko.render.geometry.stream.format.VertexFormat;
	import aerys.minko.type.math.Vector4;

	public class TorusGeometry extends Geometry
	{
		private static const EPSILON	: Number	= 0.00001;
		private static const DTOR 		: Number	= 0.01745329252;

		private var _radius		: Number;
		private var _tube		: Number;
		private var _segmentsR	: uint;
		private var _segmentsT	: uint;
		private var _arc		: Number;

		public function TorusGeometry(radius				: Number	= 100.,
									  tube					: Number	= 40.,
									  segmentsR				: uint		= 8,
									  segmentsT				: uint		= 6,
									  arc					: Number	= Math.PI * 2.,
									  withUVs				: Boolean	= true,
									  withNormals			: Boolean	= true,
									  vertexStreamUsage		: uint		= StreamUsage.STATIC,
									  indexStreamUsage		: uint		= StreamUsage.STATIC)
		{
			_radius = radius;
			_tube = tube;
			_segmentsR = segmentsR;
			_segmentsT = segmentsT;
			_arc = arc;

			super(
				new <IVertexStream>[buildVertexStream(vertexStreamUsage, withUVs, withNormals)],
				buildIndexStream(indexStreamUsage)
			);
		}

		private function buildVertexStream(usage : uint, withUVs : Boolean, withNormals : Boolean) : VertexStream
		{
			var vertexData	: Vector.<Number>	= new <Number>[];

			for (var j : uint = 0; j <= _segmentsR; ++j)
			{
				for (var i : uint = 0; i <= _segmentsT; ++i)
				{
					var u 		: Number 	= i / _segmentsT * _arc;
					var v 		: Number 	= j / _segmentsR * _arc;
					var cosU	: Number	= Math.cos(u);
					var sinU	: Number	= Math.sin(u);
					var cosV	: Number	= Math.cos(v);
					var x		: Number	= (_radius + _tube * cosV) * cosU;
					var y		: Number	= (_radius + _tube * cosV) * sinU;
					var z		: Number	= _tube * Math.sin(v);

					vertexData.push(x, y, z);

					if (withUVs)
						vertexData.push(i / _segmentsT, 1 - j / _segmentsR);

					if (withNormals)
					{
						var normalX	: Number	= x - _radius * cosU;
						var normalY	: Number	= y - _radius * sinU;
						var normalZ	: Number	= z;
						var mag		: Number	= normalX * normalX + normalY * normalY
							+ normalZ * normalZ;

						normalX /= mag;
						normalY /= mag;
						normalZ /= mag;

						vertexData.push(normalX, normalY, normalZ);
					}
				}
			}

			var format : VertexFormat = new VertexFormat(VertexComponent.XYZ);

			if (withUVs)
				format.addComponent(VertexComponent.UV);

			if (withNormals)
				format.addComponent(VertexComponent.NORMAL);

			return new VertexStream(usage, format, vertexData);
		}

		private function buildIndexStream(usage : uint) : IndexStream
		{
			var indices : Vector.<uint> = new <uint>[];

			for (var j : uint = 1; j <= _segmentsR; ++j)
			{
				for (var i : uint = 1; i <= _segmentsT; ++i)
				{
					var a : uint = (_segmentsT + 1) * j + i - 1;
					var b : uint = (_segmentsT + 1) * (j - 1) + i - 1;
					var c : uint = (_segmentsT + 1) * (j - 1) + i;
					var d : uint = (_segmentsT + 1) * j + i;

					indices.push(a, c, b, a, d, c);
				}
			}

			return new IndexStream(usage, indices);
		}
	}
}

package aerys.minko.render.geometry.primitive
{
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.geometry.stream.IVertexStream;
	import aerys.minko.render.geometry.stream.IndexStream;
	import aerys.minko.render.geometry.stream.StreamUsage;
	import aerys.minko.render.geometry.stream.VertexStream;
	import aerys.minko.render.geometry.stream.format.VertexComponent;
	import aerys.minko.render.geometry.stream.format.VertexFormat;
	import aerys.minko.type.math.Vector4;

	public class TorusGeometry extends Geometry
	{
		private static const EPSILON	: Number	= 0.00001;
		private static const DTOR 		: Number	= 0.01745329252;

		private var _radius		: Number;
		private var _tube		: Number;
		private var _segmentsR	: uint;
		private var _segmentsT	: uint;
		private var _arc		: Number;

		public function TorusGeometry(radius				: Number	= .375,
									  tube					: Number	= .125,
									  segmentsR				: uint		= 10,
									  segmentsT				: uint		= 8,
									  arc					: Number	= Math.PI * 2.,
									  withUVs				: Boolean	= true,
									  withNormals			: Boolean	= true,
									  vertexStreamUsage		: uint		= StreamUsage.STATIC,
									  indexStreamUsage		: uint		= StreamUsage.STATIC)
		{
			_radius = radius;
			_tube = tube;
			_segmentsR = segmentsR;
			_segmentsT = segmentsT;
			_arc = arc;

			super(
				new <IVertexStream>[buildVertexStream(vertexStreamUsage, withUVs, withNormals)],
				buildIndexStream(indexStreamUsage)
			);
		}

		private function buildVertexStream(usage : uint, withUVs : Boolean, withNormals : Boolean) : VertexStream
		{
			var vertexData	: Vector.<Number>	= new <Number>[];

			for (var j : uint = 0; j <= _segmentsR; ++j)
			{
				for (var i : uint = 0; i <= _segmentsT; ++i)
				{
					var u 		: Number 	= i / _segmentsT * _arc;
					var v 		: Number 	= j / _segmentsR * _arc;
					var cosU	: Number	= Math.cos(u);
					var sinU	: Number	= Math.sin(u);
					var cosV	: Number	= Math.cos(v);
					var x		: Number	= (_radius + _tube * cosV) * cosU;
					var y		: Number	= (_radius + _tube * cosV) * sinU;
					var z		: Number	= _tube * Math.sin(v);

					vertexData.push(x, y, z);

					if (withUVs)
						vertexData.push(i / _segmentsT, 1 - j / _segmentsR);

					if (withNormals)
					{
						var normalX	: Number	= x - _radius * cosU;
						var normalY	: Number	= y - _radius * sinU;
						var normalZ	: Number	= z;
						var mag		: Number	= normalX * normalX + normalY * normalY
							+ normalZ * normalZ;

						normalX /= mag;
						normalY /= mag;
						normalZ /= mag;

						vertexData.push(normalX, normalY, normalZ);
					}
				}
			}

			var format : VertexFormat = new VertexFormat(VertexComponent.XYZ);

			if (withUVs)
				format.addComponent(VertexComponent.UV);

			if (withNormals)
				format.addComponent(VertexComponent.NORMAL);

			return new VertexStream(usage, format, vertexData);
		}

		private function buildIndexStream(usage : uint) : IndexStream
		{
			var indices : Vector.<uint> = new <uint>[];

			for (var j : uint = 1; j <= _segmentsR; ++j)
			{
				for (var i : uint = 1; i <= _segmentsT; ++i)
				{
					var a : uint = (_segmentsT + 1) * j + i - 1;
					var b : uint = (_segmentsT + 1) * (j - 1) + i - 1;
					var c : uint = (_segmentsT + 1) * (j - 1) + i;
					var d : uint = (_segmentsT + 1) * j + i;

					indices.push(a, c, b, a, d, c);
				}
			}

			return new IndexStream(usage, indices);
		}
	}
}

fix TorusGeometry to be unit sized by default


/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the at.matthew.httpstreaming package.
 *
 * The Initial Developer of the Original Code is
 * Matthew Kaufman.
 * Portions created by the Initial Developer are Copyright (C) 2011
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * ***** END LICENSE BLOCK ***** */

package org.denivip.osmf.net.httpstreaming.hls
{
	import flash.utils.ByteArray;
	import flash.utils.IDataInput;

	import com.hurlant.util.Hex;
	import org.denivip.osmf.utility.decrypt.AES;
	import org.osmf.logging.Log;
	import org.osmf.logging.Logger;
	import org.osmf.net.httpstreaming.HTTPStreamingFileHandlerBase;

	[Event(name="notifySegmentDuration", type="org.osmf.events.HTTPStreamingFileHandlerEvent")]
	[Event(name="notifyTimeBias", type="org.osmf.events.HTTPStreamingFileHandlerEvent")]

	public class HTTPStreamingMP2TSFileHandler extends HTTPStreamingFileHandlerBase
	{
		private var _syncFound:Boolean;
		private var _pmtPID:uint;
		private var _audioPID:uint;
		private var _videoPID:uint;
		private var _mp3AudioPID:uint;
		private var _audioPES:HTTPStreamingMP2PESAudio;
		private var _videoPES:HTTPStreamingMP2PESVideo;
		private var _mp3audioPES:HTTPStreamingMp3Audio2ToPESAudio;

		private var _cachedOutputBytes:ByteArray;
		private var alternatingYieldCounter:int = 0;

		private var _key:HTTPStreamingM3U8IndexKey = null;
		private var _iv:ByteArray = null;
		private var _decryptBuffer:ByteArray = new ByteArray;

		// AES-128 specific variables
		private var _decryptAES:AES = null;

		public function HTTPStreamingMP2TSFileHandler()
		{
			_audioPES = new HTTPStreamingMP2PESAudio;
			_videoPES = new HTTPStreamingMP2PESVideo;
			_mp3audioPES = new HTTPStreamingMp3Audio2ToPESAudio;
		}

		override public function beginProcessFile(seek:Boolean, seekTime:Number):void
		{
			_syncFound = false;
		}

		override public function get inputBytesNeeded():Number
		{
			return _syncFound ? 187 : 1;
		}

		override public function processFileSegment(input:IDataInput):ByteArray
		{
			var bytesAvailableStart:uint = input.bytesAvailable;
			var output:ByteArray;

			if (_cachedOutputBytes !== null) {
				output = _cachedOutputBytes;
				_cachedOutputBytes = null;
			}
			else {
				output = new ByteArray();
			}

			while (true) {
				if(!_syncFound)
				{
					if (_key) {
						if (_key.type == "AES-128") {
							if (input.bytesAvailable < 16) {
								if (_decryptBuffer.bytesAvailable < 1) {
									break;
								}
							} else {
								if (!decryptToBuffer(input, 16)) {
									break;
								}
							}
							if (_decryptBuffer.readByte() == 0x47) {
								_syncFound = true;
							}
						}
					} else {
						if(input.bytesAvailable < 1)
							break;

						if(input.readByte() == 0x47)
							_syncFound = true;
					}
				}
				else
				{
					_syncFound = false;
					var packet:ByteArray = new ByteArray();

					if (_key) {
						if (_key.type == "AES-128") {
							if (input.bytesAvailable < 176) {
								if (_decryptBuffer.bytesAvailable < 187) {
									break;
								}
							} else {
								var bytesLeft:uint = input.bytesAvailable - 176;
								if (bytesLeft > 0 && bytesLeft < 15) {
									if (!decryptToBuffer(input, input.bytesAvailable)) {
										break;
									}
								} else {
									if (!decryptToBuffer(input, 176)) {
										break;
									}
								}
							}
							_decryptBuffer.readBytes(packet, 0, 187);
						}
					} else {
						if(input.bytesAvailable < 187)
							break;

						input.readBytes(packet, 0, 187);
					}

					var result:ByteArray = processPacket(packet);
					if (result !== null) {
						output.writeBytes(result);
					}

					if (bytesAvailableStart - input.bytesAvailable > 10000) {
						alternatingYieldCounter = (alternatingYieldCounter + 1) & 0x03;
						if (alternatingYieldCounter /*& 0x01 === 1*/) {
							_cachedOutputBytes = output;
							return null;
						}
						break;
					}
				}
			}
			output.position = 0;

			return output.length === 0 ? null : output;
		}

		private function decryptToBuffer(input:IDataInput, blockSize:int):Boolean{
			if (_key) {
				// Clear buffer
				if (_decryptBuffer.bytesAvailable == 0) {
					_decryptBuffer.clear();
				}

				if (_key.type == "AES-128" && blockSize % 16 == 0 && _key.key) {
					if (!_decryptAES) {
						_decryptAES = new AES(_key.key);
						_decryptAES.pad = "none";
						_decryptAES.iv = _iv;
					}

					// Save buffer position
					var currentPosition:uint = _decryptBuffer.position;
					_decryptBuffer.position += _decryptBuffer.bytesAvailable;

					// Save block to decrypt
					var decrypt:ByteArray = new ByteArray;
					input.readBytes(decrypt, 0, blockSize);
					// Save new IV from ciphertext
					var newIv:ByteArray = new ByteArray;
					decrypt.position += (decrypt.bytesAvailable-16);
					decrypt.readBytes(newIv, 0, 16);
					decrypt.position = 0;
					// Decrypt
					if (input.bytesAvailable == 0) {
						_decryptAES.pad = "pkcs7";
					}
					_decryptAES.decrypt(decrypt);
					decrypt.position = 0;
					// Write into buffer
					_decryptBuffer.writeBytes(decrypt);
					_decryptBuffer.position = currentPosition;
					// Update AES IV
					_decryptAES.iv = newIv;

					return true;
				}
			}

			return false;
		}

		override public function endProcessFile(input:IDataInput):ByteArray
		{
			_decryptBuffer.clear();
			if (_decryptAES) {
				_decryptAES.destroy();
			}
			_decryptAES = null;
			return null;
		}

		public function resetCache():void{
			_cachedOutputBytes = null;
			alternatingYieldCounter = 0;
			_decryptBuffer.clear();
			if (_decryptAES) {
				_decryptAES.destroy();
			}
			_decryptAES = null;
		}

		public function set initialOffset(offset:Number):void{
			offset *= 1000; // convert to ms
			_videoPES.initialTimestamp = offset;
			_audioPES.initialTimestamp = offset;
			_mp3audioPES.initialTimestamp = offset;
		}

		public function set key(key:HTTPStreamingM3U8IndexKey):void {
			_key = key;
			if (_decryptAES) {
				_decryptAES.destroy();
			}
			_decryptAES = null;
		}

		public function set iv(iv:String):void {
			if (iv) {
				_iv = Hex.toArray(iv);
			}
		}

		private function processPacket(packet:ByteArray):ByteArray
		{
			// decode rest of transport stream prefix (after the 0x47 flag byte)

			// top of second byte
			var value:uint = packet.readUnsignedByte();

			//var tei:Boolean = Boolean(value & 0x80);	// error indicator
			var pusi:Boolean = Boolean(value & 0x40);	// payload unit start indication
			//var tpri:Boolean = Boolean(value & 0x20);	// transport priority indication

			// bottom of second byte and all of third
			value <<= 8;
			value += packet.readUnsignedByte();

			var pid:uint = value & 0x1fff;	// packet ID

			// fourth byte
			value = packet.readUnsignedByte();
			//var scramblingControl:uint = (value >> 6) & 0x03;	// scrambling control bits
			var hasAF:Boolean = Boolean(value & 0x20);	// has adaptation field
			var hasPD:Boolean = Boolean(value & 0x10);	// has payload data
			//var ccount:uint = value & 0x0f;		// continuty count

			// technically hasPD without hasAF is an error, see spec

			if(hasAF)
			{
				// process adaptation field
				// don't care about flags
				// don't care about clocks here
                var af:uint = packet.readUnsignedByte();
				packet.position += af;	// skip to end
			}

            return hasPD ? processES(pid, pusi, packet) : null;
		}

		private function processES(pid:uint, pusi:Boolean, packet:ByteArray):ByteArray
		{
			var output:ByteArray = null;
			if(pid == 0)	// PAT
			{
				if(pusi)
					processPAT(packet);
			}
			else if(pid == _pmtPID)
			{
				if(pusi)
					processPMT(packet);
			}
			else if(pid == _audioPID)
			{
				output = _audioPES.processES(pusi, packet);
			}
			else if(pid == _videoPID)
			{
				output = _videoPES.processES(pusi, packet);
			}
			else if(pid == _mp3AudioPID)
			{
				output = _mp3audioPES.processES(pusi, packet);
			}

			return output;
		}

		private function processPAT(packet:ByteArray):void
		{
			packet.readUnsignedByte();   // pointer:uint
			packet.readUnsignedByte();   // tableID:uint
			var remaining:uint = packet.readUnsignedShort() & 0x03ff; // ignoring misc and reserved bits

			packet.position += 5; // skip tsid + version/cni + sec# + last sec#
			remaining -= 5;

			while(remaining > 4)
			{
				packet.readUnsignedShort(); // program number
				_pmtPID = packet.readUnsignedShort() & 0x1fff; // 13 bits
				remaining -= 4;

				//return; // immediately after reading the first pmt ID, if we don't we get the LAST one
			}

			// and ignore the CRC (4 bytes)
		}

		private function processPMT(packet:ByteArray):void
		{
			packet.readUnsignedByte();  // pointer:uint
			var tableID:uint = packet.readUnsignedByte();

			if (tableID != 0x02)
			{
				CONFIG::LOGGING
				{
					logger.warn("PAT pointed to PMT that isn't PMT");
				}
				return; // don't try to parse it
			}

			var remaining:uint = packet.readUnsignedShort() & 0x03ff; // ignoring section syntax and reserved

			packet.position += 7; // skip program num, rserved, version, cni, section num, last section num, reserved, PCR PID
			remaining -= 7;

			var piLen:uint = packet.readUnsignedShort() & 0x0fff;
			remaining -= 2;

			packet.position += piLen; // skip program info
			remaining -= piLen;

			while(remaining > 4)
			{
				var type:uint = packet.readUnsignedByte();
				var pid:uint = packet.readUnsignedShort() & 0x1fff;
				var esiLen:uint = packet.readUnsignedShort() & 0x0fff;
				remaining -= 5;

				packet.position += esiLen;
				remaining -= esiLen;

				switch(type)
				{
					case 0x1b: // H.264 video
						_videoPID = pid;
						break;
					case 0x0f: // AAC Audio / ADTS
						_audioPID = pid;
						break;

					case 0x03: // MP3 Audio  (3 & 4)
					case 0x04:
						_mp3AudioPID = pid;
						break;

					default:
						CONFIG::LOGGING
						{
							logger.error("unsupported type "+type.toString(16)+" in PMT");
						}
						break;
				}
			}

			// and ignore CRC
		}

		override public function flushFileSegment(input:IDataInput):ByteArray
		{
			var flvBytes:ByteArray = new ByteArray();
			var flvBytesVideo:ByteArray = _videoPES.processES(false, null, true);
			var flvBytesAudio:ByteArray = _audioPES.processES(false, null, true);

			if(flvBytesVideo)
				flvBytes.readBytes(flvBytesVideo);
			if(flvBytesAudio)
				flvBytes.readBytes(flvBytesAudio);

			return flvBytes;
		}

		CONFIG::LOGGING
		{
			private var logger:Logger = Log.getLogger('org.denivip.osmf.net.httpstreaming.hls.HTTPStreamingMP2TSFileHandler') as Logger;
		}
	}
}

/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the at.matthew.httpstreaming package.
 *
 * The Initial Developer of the Original Code is
 * Matthew Kaufman.
 * Portions created by the Initial Developer are Copyright (C) 2011
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * ***** END LICENSE BLOCK ***** */

package org.denivip.osmf.net.httpstreaming.hls
{
	import flash.utils.ByteArray;
	import flash.utils.IDataInput;

	import com.hurlant.util.Hex;
	import org.denivip.osmf.utility.decrypt.AES;
	import org.osmf.logging.Log;
	import org.osmf.logging.Logger;
	import org.osmf.net.httpstreaming.HTTPStreamingFileHandlerBase;

	[Event(name="notifySegmentDuration", type="org.osmf.events.HTTPStreamingFileHandlerEvent")]
	[Event(name="notifyTimeBias", type="org.osmf.events.HTTPStreamingFileHandlerEvent")]

	public class HTTPStreamingMP2TSFileHandler extends HTTPStreamingFileHandlerBase
	{
		private var _syncFound:Boolean;
		private var _pmtPID:uint;
		private var _audioPID:uint;
		private var _videoPID:uint;
		private var _mp3AudioPID:uint;
		private var _audioPES:HTTPStreamingMP2PESAudio;
		private var _videoPES:HTTPStreamingMP2PESVideo;
		private var _mp3audioPES:HTTPStreamingMp3Audio2ToPESAudio;

		private var _cachedOutputBytes:ByteArray;
		private var alternatingYieldCounter:int = 0;

		private var _key:HTTPStreamingM3U8IndexKey = null;
		private var _iv:ByteArray = null;
		private var _decryptBuffer:ByteArray = new ByteArray;

		// AES-128 specific variables
		private var _decryptAES:AES = null;

		public function HTTPStreamingMP2TSFileHandler()
		{
			_audioPES = new HTTPStreamingMP2PESAudio;
			_videoPES = new HTTPStreamingMP2PESVideo;
			_mp3audioPES = new HTTPStreamingMp3Audio2ToPESAudio;
		}

		override public function beginProcessFile(seek:Boolean, seekTime:Number):void
		{
			_syncFound = false;
		}

		override public function get inputBytesNeeded():Number
		{
			return _syncFound ? 187 : 1;
		}

		override public function processFileSegment(input:IDataInput):ByteArray
		{
			var bytesAvailableStart:uint = input.bytesAvailable;
			var output:ByteArray;

			if (_cachedOutputBytes !== null) {
				output = _cachedOutputBytes;
				_cachedOutputBytes = null;
			}
			else {
				output = new ByteArray();
			}

			while (true) {
				if(!_syncFound)
				{
					if (_key) {
						if (_key.type == "AES-128") {
							if (input.bytesAvailable < 16) {
								if (_decryptBuffer.bytesAvailable < 1) {
									break;
								}
							} else {
								if (!decryptToBuffer(input, 16)) {
									break;
								}
							}
							if (_decryptBuffer.readByte() == 0x47) {
								_syncFound = true;
							}
						}
					} else {
						if(input.bytesAvailable < 1)
							break;

						if(input.readByte() == 0x47)
							_syncFound = true;
					}
				}
				else
				{
					var packet:ByteArray = new ByteArray();

					if (_key) {
						if (_key.type == "AES-128") {
							if (input.bytesAvailable < 176) {
								if (_decryptBuffer.bytesAvailable < 187) {
									break;
								}
							} else {
								var bytesLeft:uint = input.bytesAvailable - 176;
								if (bytesLeft > 0 && bytesLeft < 15) {
									if (!decryptToBuffer(input, input.bytesAvailable)) {
										break;
									}
								} else {
									if (!decryptToBuffer(input, 176)) {
										break;
									}
								}
							}
							_decryptBuffer.readBytes(packet, 0, 187);
						}
					} else {
						if(input.bytesAvailable < 187)
							break;

						input.readBytes(packet, 0, 187);
					}

					_syncFound = false;
					var result:ByteArray = processPacket(packet);
					if (result !== null) {
						output.writeBytes(result);
					}

					if (bytesAvailableStart - input.bytesAvailable > 10000) {
						alternatingYieldCounter = (alternatingYieldCounter + 1) & 0x03;
						if (alternatingYieldCounter /*& 0x01 === 1*/) {
							_cachedOutputBytes = output;
							return null;
						}
						break;
					}
				}
			}
			output.position = 0;

			return output.length === 0 ? null : output;
		}

		private function decryptToBuffer(input:IDataInput, blockSize:int):Boolean{
			if (_key) {
				// Clear buffer
				if (_decryptBuffer.bytesAvailable == 0) {
					_decryptBuffer.clear();
				}

				if (_key.type == "AES-128" && blockSize % 16 == 0 && _key.key) {
					if (!_decryptAES) {
						_decryptAES = new AES(_key.key);
						_decryptAES.pad = "none";
						_decryptAES.iv = _iv;
					}

					// Save buffer position
					var currentPosition:uint = _decryptBuffer.position;
					_decryptBuffer.position += _decryptBuffer.bytesAvailable;

					// Save block to decrypt
					var decrypt:ByteArray = new ByteArray;
					input.readBytes(decrypt, 0, blockSize);
					// Save new IV from ciphertext
					var newIv:ByteArray = new ByteArray;
					decrypt.position += (decrypt.bytesAvailable-16);
					decrypt.readBytes(newIv, 0, 16);
					decrypt.position = 0;
					// Decrypt
					if (input.bytesAvailable == 0) {
						_decryptAES.pad = "pkcs7";
					}
					_decryptAES.decrypt(decrypt);
					decrypt.position = 0;
					// Write into buffer
					_decryptBuffer.writeBytes(decrypt);
					_decryptBuffer.position = currentPosition;
					// Update AES IV
					_decryptAES.iv = newIv;

					return true;
				}
			}

			return false;
		}

		override public function endProcessFile(input:IDataInput):ByteArray
		{
			_decryptBuffer.clear();
			if (_decryptAES) {
				_decryptAES.destroy();
			}
			_decryptAES = null;
			return null;
		}

		public function resetCache():void{
			_cachedOutputBytes = null;
			alternatingYieldCounter = 0;
			_decryptBuffer.clear();
			if (_decryptAES) {
				_decryptAES.destroy();
			}
			_decryptAES = null;
		}

		public function set initialOffset(offset:Number):void{
			offset *= 1000; // convert to ms
			_videoPES.initialTimestamp = offset;
			_audioPES.initialTimestamp = offset;
			_mp3audioPES.initialTimestamp = offset;
		}

		public function set key(key:HTTPStreamingM3U8IndexKey):void {
			_key = key;
			if (_decryptAES) {
				_decryptAES.destroy();
			}
			_decryptAES = null;
		}

		public function set iv(iv:String):void {
			if (iv) {
				_iv = Hex.toArray(iv);
			}
		}

		private function processPacket(packet:ByteArray):ByteArray
		{
			// decode rest of transport stream prefix (after the 0x47 flag byte)

			// top of second byte
			var value:uint = packet.readUnsignedByte();

			//var tei:Boolean = Boolean(value & 0x80);	// error indicator
			var pusi:Boolean = Boolean(value & 0x40);	// payload unit start indication
			//var tpri:Boolean = Boolean(value & 0x20);	// transport priority indication

			// bottom of second byte and all of third
			value <<= 8;
			value += packet.readUnsignedByte();

			var pid:uint = value & 0x1fff;	// packet ID

			// fourth byte
			value = packet.readUnsignedByte();
			//var scramblingControl:uint = (value >> 6) & 0x03;	// scrambling control bits
			var hasAF:Boolean = Boolean(value & 0x20);	// has adaptation field
			var hasPD:Boolean = Boolean(value & 0x10);	// has payload data
			//var ccount:uint = value & 0x0f;		// continuty count

			// technically hasPD without hasAF is an error, see spec

			if(hasAF)
			{
				// process adaptation field
				// don't care about flags
				// don't care about clocks here
                var af:uint = packet.readUnsignedByte();
				packet.position += af;	// skip to end
			}

            return hasPD ? processES(pid, pusi, packet) : null;
		}

		private function processES(pid:uint, pusi:Boolean, packet:ByteArray):ByteArray
		{
			var output:ByteArray = null;
			if(pid == 0)	// PAT
			{
				if(pusi)
					processPAT(packet);
			}
			else if(pid == _pmtPID)
			{
				if(pusi)
					processPMT(packet);
			}
			else if(pid == _audioPID)
			{
				output = _audioPES.processES(pusi, packet);
			}
			else if(pid == _videoPID)
			{
				output = _videoPES.processES(pusi, packet);
			}
			else if(pid == _mp3AudioPID)
			{
				output = _mp3audioPES.processES(pusi, packet);
			}

			return output;
		}

		private function processPAT(packet:ByteArray):void
		{
			packet.readUnsignedByte();   // pointer:uint
			packet.readUnsignedByte();   // tableID:uint
			var remaining:uint = packet.readUnsignedShort() & 0x03ff; // ignoring misc and reserved bits

			packet.position += 5; // skip tsid + version/cni + sec# + last sec#
			remaining -= 5;

			while(remaining > 4)
			{
				packet.readUnsignedShort(); // program number
				_pmtPID = packet.readUnsignedShort() & 0x1fff; // 13 bits
				remaining -= 4;

				//return; // immediately after reading the first pmt ID, if we don't we get the LAST one
			}

			// and ignore the CRC (4 bytes)
		}

		private function processPMT(packet:ByteArray):void
		{
			packet.readUnsignedByte();  // pointer:uint
			var tableID:uint = packet.readUnsignedByte();

			if (tableID != 0x02)
			{
				CONFIG::LOGGING
				{
					logger.warn("PAT pointed to PMT that isn't PMT");
				}
				return; // don't try to parse it
			}

			var remaining:uint = packet.readUnsignedShort() & 0x03ff; // ignoring section syntax and reserved

			packet.position += 7; // skip program num, rserved, version, cni, section num, last section num, reserved, PCR PID
			remaining -= 7;

			var piLen:uint = packet.readUnsignedShort() & 0x0fff;
			remaining -= 2;

			packet.position += piLen; // skip program info
			remaining -= piLen;

			while(remaining > 4)
			{
				var type:uint = packet.readUnsignedByte();
				var pid:uint = packet.readUnsignedShort() & 0x1fff;
				var esiLen:uint = packet.readUnsignedShort() & 0x0fff;
				remaining -= 5;

				packet.position += esiLen;
				remaining -= esiLen;

				switch(type)
				{
					case 0x1b: // H.264 video
						_videoPID = pid;
						break;
					case 0x0f: // AAC Audio / ADTS
						_audioPID = pid;
						break;

					case 0x03: // MP3 Audio  (3 & 4)
					case 0x04:
						_mp3AudioPID = pid;
						break;

					default:
						CONFIG::LOGGING
						{
							logger.error("unsupported type "+type.toString(16)+" in PMT");
						}
						break;
				}
			}

			// and ignore CRC
		}

		override public function flushFileSegment(input:IDataInput):ByteArray
		{
			var flvBytes:ByteArray = new ByteArray();
			var flvBytesVideo:ByteArray = _videoPES.processES(false, null, true);
			var flvBytesAudio:ByteArray = _audioPES.processES(false, null, true);

			if(flvBytesVideo)
				flvBytes.readBytes(flvBytesVideo);
			if(flvBytesAudio)
				flvBytes.readBytes(flvBytesAudio);

			return flvBytes;
		}

		CONFIG::LOGGING
		{
			private var logger:Logger = Log.getLogger('org.denivip.osmf.net.httpstreaming.hls.HTTPStreamingMP2TSFileHandler') as Logger;
		}
	}
}

Fix bug where syncFound was flagged as false incorrectly

This was causing byte-level sync issues with throttled (and potentially
other) streams


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mangui.hls.demux {

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

    /** Constants and utilities for the H264 video format. **/
    public class Nalu {

		private static var scratchEscapePositions:Array = new Array(10);

        private static var _audNalu : ByteArray;
        // static initializer
        {
            _audNalu = new ByteArray();
            _audNalu.length = 2;
            _audNalu.writeByte(0x09);
            _audNalu.writeByte(0xF0);
        };

        /** Return an array with NAL delimiter indexes. **/
        public static function getNALU(nalu : ByteArray, position : uint) : Vector.<VideoFrame> {
            var len : uint = nalu.length,i : uint = position;
            var unitHeader : int,lastUnitHeader : int = 0;
            var unitStart : int,lastUnitStart : int = 0;
            var unitType : int,lastUnitType : int = 0;
            var audFound : Boolean = false;
            var value : uint,state : uint = 0;
            var units : Vector.<VideoFrame> = new Vector.<VideoFrame>();
            // Loop through data to find NAL startcodes.
            while (i < len) {
                // finding 3 or 4-byte start codes (00 00 01 OR 00 00 00 01)
                value = nalu[i++];
                switch(state)
                {
                    case 0:
                        if(!value) {
                            state = 1;
                            // unitHeader is NAL header offset
                            unitHeader=i-1;
                        }
                        break;
                    case 1:
                        if(value) {
                            state = 0;
                        } else {
                            state = 2;
                        }
                        break;
                    case 2:
                    case 3:
                        if(value) {
                            if(value === 1 && i < len) {
                                unitType = nalu[i] & 0x1f;
                                if(unitType == 9) {
                                    audFound = true;
                                }
                                if(lastUnitStart) {
                                    // use Math.min(4,...) as max header size is 4.
                                    // in case there are any leading zeros
                                    // such as 00 00 00 00 00 00 01
                                    //                  ^^
                                    // we need to ignore them as they are part of previous NAL unit
                                    units.push(new VideoFrame(Math.min(4,lastUnitStart-lastUnitHeader), i-state-1-lastUnitStart, lastUnitStart, lastUnitType));
                                }
                                lastUnitStart = i;
                                lastUnitType = unitType;
                                lastUnitHeader = unitHeader;
                                if(audFound == true && (unitType === 1 || unitType === 5)) {
                                  // OPTI !!! if AUD unit already parsed and if IDR/NDR unit, consider it is last NALu
                                  i = len;
                                }
                            }
                            state = 0;
                        } else {
                            state = 3;
                        }
                        break;
                    default:
                        break;
                }
            }
            //push last unit
            if(lastUnitStart) {
                units.push(new VideoFrame(Math.min(4,lastUnitStart-lastUnitHeader), len-lastUnitStart, lastUnitStart, lastUnitType));
            }
            // Reset position and return results.
            CONFIG::LOGGING {
                if (HLSSettings.logDebug2) {
                    /** H264 NAL unit names. **/
                    const NAMES : Array = ['Unspecified',// 0
                    'NDR',                          // 1
                    'Partition A',                  // 2
                    'Partition B',                  // 3
                    'Partition C',                  // 4
                    'IDR',                          // 5
                    'SEI',                          // 6
                    'SPS',                          // 7
                    'PPS',                          // 8
                    'AUD',                          // 9
                    'End of Sequence',              // 10
                    'End of Stream',                // 11
                    'Filler Data'// 12
                    ];
                    if (units.length) {
                        var txt : String = "AVC: ";
                        for (i = 0; i < units.length; i++) {
                            txt += NAMES[units[i].type] + ","; //+ ":" + units[i].length
                        }
                        Log.debug2(txt.substr(0,txt.length-1) + " slices");
                    } else {
                        Log.debug2('AVC: no NALU slices found');
                    }
                }
            }
            nalu.position = position;
            return units;
        };

        public static function get AUD():ByteArray {
            return _audNalu;
        }

		private static function findNextUnescapeIndex(bytes : ByteArray, offset:int, limit:int):int {
			for (var i:int = offset; i < limit - 2; i++) {
				if (bytes[i] == 0x00 && bytes[i + 1] == 0x00 && bytes[i + 2] == 0x03) {
					return i;
				}
			}
			return limit;
		}

		private static function arrayCopy(src:ByteArray,  srcPos:int, dest:ByteArray, destPos:int, length:int):void {
			var iterations:int = Math.min(Math.min(length, src.length - srcPos), dest.length - destPos);
			for(var i:int = 0; i < iterations; i++)
				dest[destPos + i] = src[srcPos + i];
		}

		public static function unescapeStream(data : ByteArray, position: int,limit: uint):int {
			var scratchEscapeCount:int = 0;

			while (position < limit) {
				position = findNextUnescapeIndex(data, position, limit);
				if (position < limit) {
					if (scratchEscapePositions.length <= scratchEscapeCount) {
						// Grow scratchEscapePositions to hold a larger number of positions.
						scratchEscapePositions = scratchEscapePositions.concat(new Array(10));
					}
					scratchEscapePositions[scratchEscapeCount++] = position;
					position += 3;
				}
			}

			var unescapedLength:int = limit - scratchEscapeCount;
			var escapedPosition:int = 0; // The position being read from.
			var unescapedPosition:int = 0; // The position being written to.
			for (var i:int = 0; i < scratchEscapeCount; i++) {
				var nextEscapePosition:int = scratchEscapePositions[i];
				var copyLength:int = nextEscapePosition - escapedPosition;
				arrayCopy(data, escapedPosition, data, unescapedPosition, copyLength);
				unescapedPosition += copyLength;
				data[unescapedPosition++] = 0;
				data[unescapedPosition++] = 0;
				escapedPosition += copyLength + 3;
			}

			var remainingLength:int = unescapedLength - unescapedPosition;
			arrayCopy(data, escapedPosition, data, unescapedPosition, remainingLength);

			return unescapedLength;
		}
    }
}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mangui.hls.demux {

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

    /** Constants and utilities for the H264 video format. **/
    public class Nalu {

        private static var _audNalu : ByteArray;
        // static initializer
        {
            _audNalu = new ByteArray();
            _audNalu.length = 2;
            _audNalu.writeByte(0x09);
            _audNalu.writeByte(0xF0);
        };

        /** Return an array with NAL delimiter indexes. **/
        public static function getNALU(nalu : ByteArray, position : uint) : Vector.<VideoFrame> {
            var len : uint = nalu.length,i : uint = position;
            var unitHeader : int,lastUnitHeader : int = 0;
            var unitStart : int,lastUnitStart : int = 0;
            var unitType : int,lastUnitType : int = 0;
            var audFound : Boolean = false;
            var value : uint,state : uint = 0;
            var units : Vector.<VideoFrame> = new Vector.<VideoFrame>();
            // Loop through data to find NAL startcodes.
            while (i < len) {
                // finding 3 or 4-byte start codes (00 00 01 OR 00 00 00 01)
                value = nalu[i++];
                switch(state)
                {
                    case 0:
                        if(!value) {
                            state = 1;
                            // unitHeader is NAL header offset
                            unitHeader=i-1;
                        }
                        break;
                    case 1:
                        if(value) {
                            state = 0;
                        } else {
                            state = 2;
                        }
                        break;
                    case 2:
                    case 3:
                        if(value) {
                            if(value === 1 && i < len) {
                                unitType = nalu[i] & 0x1f;
                                if(unitType == 9) {
                                    audFound = true;
                                }
                                if(lastUnitStart) {
                                    // use Math.min(4,...) as max header size is 4.
                                    // in case there are any leading zeros
                                    // such as 00 00 00 00 00 00 01
                                    //                  ^^
                                    // we need to ignore them as they are part of previous NAL unit
                                    units.push(new VideoFrame(Math.min(4,lastUnitStart-lastUnitHeader), i-state-1-lastUnitStart, lastUnitStart, lastUnitType));
                                }
                                lastUnitStart = i;
                                lastUnitType = unitType;
                                lastUnitHeader = unitHeader;
                                if(audFound == true && (unitType === 1 || unitType === 5)) {
                                  // OPTI !!! if AUD unit already parsed and if IDR/NDR unit, consider it is last NALu
                                  i = len;
                                }
                            }
                            state = 0;
                        } else {
                            state = 3;
                        }
                        break;
                    default:
                        break;
                }
            }
            //push last unit
            if(lastUnitStart) {
                units.push(new VideoFrame(Math.min(4,lastUnitStart-lastUnitHeader), len-lastUnitStart, lastUnitStart, lastUnitType));
            }
            // Reset position and return results.
            CONFIG::LOGGING {
                if (HLSSettings.logDebug2) {
                    /** H264 NAL unit names. **/
                    const NAMES : Array = ['Unspecified',// 0
                    'NDR',                          // 1
                    'Partition A',                  // 2
                    'Partition B',                  // 3
                    'Partition C',                  // 4
                    'IDR',                          // 5
                    'SEI',                          // 6
                    'SPS',                          // 7
                    'PPS',                          // 8
                    'AUD',                          // 9
                    'End of Sequence',              // 10
                    'End of Stream',                // 11
                    'Filler Data'// 12
                    ];
                    if (units.length) {
                        var txt : String = "AVC: ";
                        for (i = 0; i < units.length; i++) {
                            txt += NAMES[units[i].type] + ","; //+ ":" + units[i].length
                        }
                        Log.debug2(txt.substr(0,txt.length-1) + " slices");
                    } else {
                        Log.debug2('AVC: no NALU slices found');
                    }
                }
            }
            nalu.position = position;
            return units;
        };

        public static function get AUD():ByteArray {
            return _audNalu;
        }

		private static function findNextUnescapeIndex(bytes : ByteArray, offset:int, limit:int):int {
			for (var i:int = offset; i < limit - 2; i++) {
				if (bytes[i] == 0x00 && bytes[i + 1] == 0x00 && bytes[i + 2] == 0x03) {
					return i;
				}
			}
			return limit;
		}

		private static function arrayCopy(src:ByteArray,  srcPos:int, dest:ByteArray, destPos:int, length:int):void {
			var iterations:int = Math.min(Math.min(length, src.length - srcPos), dest.length - destPos);
			for(var i:int = 0; i < iterations; i++)
				dest[destPos + i] = src[srcPos + i];
		}

		public static function unescapeStream(data : ByteArray, position: int,limit: uint):int {
			var scratchEscapeCount:int = 0;
			var scratchEscapePositions:Array = new Array(10);

			while (position < limit) {
				position = findNextUnescapeIndex(data, position, limit);
				if (position < limit) {
					if (scratchEscapePositions.length <= scratchEscapeCount) {
						// Grow scratchEscapePositions to hold a larger number of positions.
						scratchEscapePositions = scratchEscapePositions.concat(new Array(10));
					}
					scratchEscapePositions[scratchEscapeCount++] = position;
					position += 3;
				}
			}

			var unescapedLength:int = limit - scratchEscapeCount;
			var escapedPosition:int = 0; // The position being read from.
			var unescapedPosition:int = 0; // The position being written to.
			for (var i:int = 0; i < scratchEscapeCount; i++) {
				var nextEscapePosition:int = scratchEscapePositions[i];
				var copyLength:int = nextEscapePosition - escapedPosition;
				arrayCopy(data, escapedPosition, data, unescapedPosition, copyLength);
				unescapedPosition += copyLength;
				data[unescapedPosition++] = 0;
				data[unescapedPosition++] = 0;
				escapedPosition += copyLength + 3;
			}

			var remainingLength:int = unescapedLength - unescapedPosition;
			arrayCopy(data, escapedPosition, data, unescapedPosition, remainingLength);

			return unescapedLength;
		}
    }
}

move scratchEscapePositions var inside unescapeStream method