package com.kaltura.kdpfl.plugin.component
{
	import com.kaltura.kdpfl.model.ConfigProxy;
	import com.kaltura.kdpfl.model.MediaProxy;
	import com.kaltura.kdpfl.model.SequenceProxy;
	import com.kaltura.kdpfl.model.type.EnableType;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.view.controls.KTrace;
	import com.kaltura.types.KalturaMediaType;
	import com.kaltura.vo.KalturaMediaEntry;

	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.TimerEvent;
	import flash.external.ExternalInterface;
	import flash.net.SharedObject;
	import flash.net.URLRequest;
	import flash.utils.Timer;

	import org.puremvc.as3.interfaces.INotification;
	import org.puremvc.as3.patterns.mediator.Mediator;

	public class BitrateDetectionMediator extends Mediator
	{
		public static var NAME:String = "bitrateDetectionMediator";
		/**
		 * Flag indicating autoPlay flashvar was true
		 */
		private var _wasAutoPlay:Boolean = false;
		/**
		 * Flag indicating singleAutoPlay was true
		 */
		private var _wasSingleAutoPlay:Boolean = false;
		/**
		 * The url loader
		 */
		private var _loader:Loader;
		/**
		 * average download speed
		 */
		private var _avgSpeed:Number = 0;
		/**
		 * counter for progress events, will be used to calculate average speed
		 */
		private var _progressCount:int;
		/**
		 * timer for the download process
		 */
		private var _downloadTimer:Timer;
		/**
		 * startTime of the download process
		 */
		private var _startTime:Number;
		/**
		 * Indicating if player has already played
		 */
		private var _playedPlayed:Boolean = false;

		private var _configProxy:ConfigProxy;

		private var _prevTime:Number;
		private var _prevBytesLoaded:int;
		private var _forceBitrate:int;

		public function BitrateDetectionMediator(viewComponentObject:Object = null , forceBitrate:int = 0)
		{
			_forceBitrate = forceBitrate;
			super(NAME, viewComponentObject);
		}

		override public function listNotificationInterests():Array
		{
			return [
				NotificationType.DO_SWITCH,
				NotificationType.KDP_EMPTY,
				NotificationType.KDP_READY,
				NotificationType.PLAYER_PLAYED,
				NotificationType.MEDIA_READY
				];
		}

		private var _bandwidth:Number = 0;
		private var _bandwidthByUser:Number = 0;

		override public function handleNotification(notification:INotification):void
		{
			//check bitrate only before player played
/*			if (_playedPlayed)
			{
				return;
			}*/

			switch (notification.getName())
			{
				//in case the user switched the flavor manually
				case NotificationType.DO_SWITCH:
					_bandwidthByUser = int(notification.getBody())
					break;
				case NotificationType.MEDIA_READY:
					if(_bandwidthByUser)
					{
						sendNotification(NotificationType.CHANGE_PREFERRED_BITRATE, {bitrate: _bandwidthByUser});
						return;
					}
					if(_bandwidth)
						sendNotification(NotificationType.CHANGE_PREFERRED_BITRATE, {bitrate: _bandwidth});
					break;
				case NotificationType.KDP_EMPTY:
				case NotificationType.KDP_READY:
					var mediaProxy:MediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
					if (!mediaProxy.vo.entry ||
						((mediaProxy.vo.entry is KalturaMediaEntry) && (int(mediaProxy.vo.entry.mediaType)==KalturaMediaType.IMAGE)))
						break;
					trace("bitrate detection:", notification.getName());
					_configProxy = facade.retrieveProxy(ConfigProxy.NAME) as ConfigProxy;
					if ((viewComponent as bitrateDetectionPluginCode).useFlavorCookie)
					{
						var flavorCookie : SharedObject;
						try
						{
							//Check to see if we have a cookie
							flavorCookie = SharedObject.getLocal("kaltura");
						}
						catch (e: Error)
						{
							//if not just start download
							trace ("no permissions to access partner's file system");
							startDownload();
							return;
						}
						if (flavorCookie && flavorCookie.data)
						{
							//If we are in Auto Switch or the first time we run it - do the download test
							if(!flavorCookie.data.preferedFlavorBR || (flavorCookie.data.preferedFlavorBR == -1))
							{
								startDownload();
							}
						}
					}
					//disable bitrate cookie--> start detection
					else
					{
						startDownload();
					}
					break;
				case NotificationType.PLAYER_PLAYED:
					if(_bandwidth)
						return;
					_playedPlayed = true;
					break;
			}
		}

		/**
		 * Start a download process to find the preferred bitrate
		 *
		 */
		public function startDownload() : void
		{
			trace ("bitrate detection: start download");
			if (!(viewComponent as bitrateDetectionPluginCode).downloadUrl)
				return;

			var mediaProxy:MediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
			//disable autoPlay - will change it back once the bitrate detection will finish
			if (_configProxy.vo.flashvars.autoPlay == "true")
			{
				trace ("bitrate detection: was auto play");
				_configProxy.vo.flashvars.autoPlay = "false";
				_wasAutoPlay = true;
			}
			if (mediaProxy.vo.singleAutoPlay)
			{
				mediaProxy.vo.singleAutoPlay = false;
				_wasSingleAutoPlay = true;
			}
			_startTime = ( new Date( ) ).getTime( );
			_progressCount = 0;
			_prevTime = _startTime;
			_prevBytesLoaded = 0;
			_loader = new Loader( );
			_loader.contentLoaderInfo.addEventListener( Event.COMPLETE, downloadComplete );
			_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
			_loader.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, ioErrorHandler );
			_downloadTimer = new Timer ((viewComponent as bitrateDetectionPluginCode).downloadTimeoutMS, 1);
			_downloadTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
			sendNotification(NotificationType.ENABLE_GUI, {guiEnabled: false, enableType: EnableType.FULL});
			sendNotification( NotificationType.SWITCHING_CHANGE_STARTED, {newIndex: -2,  newBitrate: null});
			_loader.load( new URLRequest( (viewComponent as bitrateDetectionPluginCode).downloadUrl + "?" + ( Math.random( ) * 100000 )) );
			_downloadTimer.start();
		}

		/**
		 * on download progress- calculate average speed
		 * @param event
		 *
		 */
		private function onProgress (event:ProgressEvent):void
		{
			var curTime:Number = ( new Date( ) ).getTime( );
			_progressCount++;
			if (event.bytesLoaded!=0)
			{
				var totalTime:Number =( curTime - _startTime ) / 1000;
				var totalKB:Number = event.bytesLoaded / 1024;
				_avgSpeed = totalKB / totalTime;
			}
			_prevTime = curTime;
			_prevBytesLoaded = event.bytesLoaded;
		}

		/**
		 *  download complete handler - set the preferred bitrate, enable GUI
		 *
		 */
		private function downloadComplete( e:Event = null):void
		{
			_downloadTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
			_loader.contentLoaderInfo.removeEventListener( ProgressEvent.PROGRESS, onProgress );

			var bitrateVal:int = _avgSpeed * 8;
			trace("*** preferred bitrate for bitrate detection plugin:", bitrateVal);

			//for debugging - force a bitrate value to override the calculated one
			if(_forceBitrate > 0)
			{
				bitrateVal = _forceBitrate;
			}

			//for testing - expose the # via JS
			try
			{
				ExternalInterface.call('bitrateValue' , bitrateVal);
			}
			catch(error:Error)
			{

			}

			sendNotification(NotificationType.CHANGE_PREFERRED_BITRATE, {bitrate: bitrateVal});

			_bandwidth = bitrateVal;

			//write to cookie
			if (_configProxy.vo.flashvars.allowCookies=="true")
			{
				var flavorCookie : SharedObject;
				try
				{
					flavorCookie = SharedObject.getLocal("kaltura");
				}
				catch (e : Error)
				{
					trace("No access to user's file system");
				}
				if (flavorCookie && flavorCookie.data)
				{
					flavorCookie.data.preferedFlavorBR = bitrateVal;
					flavorCookie.flush();
				}
			}

			finishDownloadProcess();
		}

		/**
		 * default timer time has passed, stop listening to progress event and continue the flow
		 * @param event
		 *
		 */
		private function onTimerComplete (event:TimerEvent):void {
			downloadComplete();
		}

		/**
		 * I/O error getting the sample file, release the UI
		 * @param event
		 *
		 */
		private function ioErrorHandler(event:IOErrorEvent) : void
		{
			//Bypass: ignore #2124 error (loaded file is an unknown type)
			if (!event.text || event.text.indexOf("Error #2124")==-1)
			{
				trace ("bitrate detection i/o error:", event.text);
				finishDownloadProcess();
			}
		}

		/**
		 * enable back the GUI.
		 * call Do_Play if we were in auto play and set back relevant flashvars
		 *
		 */
		private function finishDownloadProcess():void {
			//if we changed variables, set them back
			if (_wasAutoPlay || _wasSingleAutoPlay) {
				var mediaProxy:MediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
				var sequenceProxy:SequenceProxy = facade.retrieveProxy(SequenceProxy.NAME) as SequenceProxy;
				if (_wasAutoPlay)
				{
					_configProxy.vo.flashvars.autoPlay = "true";
				}
				else
				{
					mediaProxy.vo.singleAutoPlay = true;
				}
				//play if we should have played
				if (!_playedPlayed && !mediaProxy.vo.isMediaDisabled && !sequenceProxy.vo.isInSequence)
				{
					sendNotification(NotificationType.DO_PLAY);
				}
			}

			sendNotification(NotificationType.ENABLE_GUI, {guiEnabled: true, enableType: EnableType.FULL});
		}

	}

}

package com.kaltura.kdpfl.plugin.component
{
	import com.kaltura.kdpfl.model.ConfigProxy;
	import com.kaltura.kdpfl.model.MediaProxy;
	import com.kaltura.kdpfl.model.SequenceProxy;
	import com.kaltura.kdpfl.model.type.EnableType;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.view.controls.KTrace;
	import com.kaltura.types.KalturaMediaType;
	import com.kaltura.vo.KalturaMediaEntry;

	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.TimerEvent;
	import flash.external.ExternalInterface;
	import flash.net.SharedObject;
	import flash.net.URLRequest;
	import flash.utils.Timer;

	import org.puremvc.as3.interfaces.INotification;
	import org.puremvc.as3.patterns.mediator.Mediator;

	public class BitrateDetectionMediator extends Mediator
	{
		public static var NAME:String = "bitrateDetectionMediator";
		/**
		 * Flag indicating autoPlay flashvar was true
		 */
		private var _wasAutoPlay:Boolean = false;
		/**
		 * Flag indicating singleAutoPlay was true
		 */
		private var _wasSingleAutoPlay:Boolean = false;
		/**
		 * The url loader
		 */
		private var _loader:Loader;
		/**
		 * average download speed
		 */
		private var _avgSpeed:Number = 0;
		/**
		 * counter for progress events, will be used to calculate average speed
		 */
		private var _progressCount:int;
		/**
		 * timer for the download process
		 */
		private var _downloadTimer:Timer;
		/**
		 * startTime of the download process
		 */
		private var _startTime:Number;
		/**
		 * Indicating if player has already played
		 */
		private var _playedPlayed:Boolean = false;

		private var _configProxy:ConfigProxy;

		private var _prevTime:Number;
		private var _prevBytesLoaded:int;
		private var _forceBitrate:int;

		public function BitrateDetectionMediator(viewComponentObject:Object = null , forceBitrate:int = 0)
		{
			_forceBitrate = forceBitrate;
			super(NAME, viewComponentObject);
		}

		override public function listNotificationInterests():Array
		{
			return [
				NotificationType.DO_SWITCH,
				NotificationType.KDP_EMPTY,
				NotificationType.KDP_READY,
				NotificationType.PLAYER_PLAYED,
				NotificationType.MEDIA_READY
				];
		}

		private var _bandwidth:Number = 0;
		private var _bandwidthByUser:Number = 0;

		override public function handleNotification(notification:INotification):void
		{
			//check bitrate only before player played
/*			if (_playedPlayed)
			{
				return;
			}*/

			switch (notification.getName())
			{
				//in case the user switched the flavor manually
				case NotificationType.DO_SWITCH:
					_bandwidthByUser = int(notification.getBody())
					break;
				case NotificationType.MEDIA_READY:
					if(_bandwidthByUser)
					{
						sendNotification(NotificationType.CHANGE_PREFERRED_BITRATE, {bitrate: _bandwidthByUser});
						return;
					}
					if(_bandwidth)
						sendNotification(NotificationType.CHANGE_PREFERRED_BITRATE, {bitrate: _bandwidth});
					break;
				case NotificationType.KDP_EMPTY:
				case NotificationType.KDP_READY:
					var mediaProxy:MediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
					if (!mediaProxy.vo.entry ||
						((mediaProxy.vo.entry is KalturaMediaEntry) && (int(mediaProxy.vo.entry.mediaType)==KalturaMediaType.IMAGE)))
						break;
					trace("bitrate detection:", notification.getName());
					_configProxy = facade.retrieveProxy(ConfigProxy.NAME) as ConfigProxy;
					if ((viewComponent as bitrateDetectionPluginCode).useFlavorCookie)
					{
						var flavorCookie : SharedObject;
						try
						{
							//Check to see if we have a cookie
							flavorCookie = SharedObject.getLocal("kaltura");
						}
						catch (e: Error)
						{
							//if not just start download
							trace ("no permissions to access partner's file system");
							startDownload();
							return;
						}
						if (flavorCookie && flavorCookie.data)
						{
							//If we are in Auto Switch or the first time we run it - do the download test
							if(!flavorCookie.data.preferedFlavorBR || (flavorCookie.data.preferedFlavorBR == -1))
							{
								startDownload();
							}
						}
					}
					//disable bitrate cookie--> start detection
					else
					{
						startDownload();
					}
					break;
				case NotificationType.PLAYER_PLAYED:
					if(_bandwidth)
						return;
					_playedPlayed = true;
					break;
			}
		}

		/**
		 * Start a download process to find the preferred bitrate
		 *
		 */
		public function startDownload() : void
		{
			trace ("bitrate detection: start download");
			if (!(viewComponent as bitrateDetectionPluginCode).downloadUrl)
				return;

			var mediaProxy:MediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
			//disable autoPlay - will change it back once the bitrate detection will finish
			if (_configProxy.vo.flashvars.autoPlay == "true")
			{
				trace ("bitrate detection: was auto play");
				_configProxy.vo.flashvars.autoPlay = "false";
				_wasAutoPlay = true;
			}
			if (mediaProxy.vo.singleAutoPlay)
			{
				mediaProxy.vo.singleAutoPlay = false;
				_wasSingleAutoPlay = true;
			}
			_startTime = ( new Date( ) ).getTime( );
			_progressCount = 0;
			_prevTime = _startTime;
			_prevBytesLoaded = 0;
			_loader = new Loader( );
			_loader.contentLoaderInfo.addEventListener( Event.COMPLETE, downloadComplete );
			_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
			_loader.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, ioErrorHandler );
			_downloadTimer = new Timer ((viewComponent as bitrateDetectionPluginCode).downloadTimeoutMS, 1);
			_downloadTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
			sendNotification(NotificationType.ENABLE_GUI, {guiEnabled: false, enableType: EnableType.FULL});
			sendNotification( NotificationType.SWITCHING_CHANGE_STARTED, {newIndex: -2,  newBitrate: null});
			_loader.load( new URLRequest( (viewComponent as bitrateDetectionPluginCode).downloadUrl + "?" + ( Math.random( ) * 100000 )) );
			_downloadTimer.start();
		}

		/**
		 * on download progress- calculate average speed
		 * @param event
		 *
		 */
		private function onProgress (event:ProgressEvent):void
		{
			var curTime:Number = ( new Date( ) ).getTime( );
			_progressCount++;
			if (event.bytesLoaded!=0)
			{
				var totalTime:Number =( curTime - _startTime ) / 1000;
				var totalKB:Number = event.bytesLoaded / 1024;
				_avgSpeed = totalKB / totalTime;
			}
			_prevTime = curTime;
			_prevBytesLoaded = event.bytesLoaded;
		}

		/**
		 *  download complete handler - set the preferred bitrate, enable GUI
		 *
		 */
		private function downloadComplete( e:Event = null):void
		{
			_downloadTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
			_loader.contentLoaderInfo.removeEventListener( ProgressEvent.PROGRESS, onProgress );

			var bitrateVal:int = _avgSpeed * 8;
			trace("*** preferred bitrate for bitrate detection plugin:", bitrateVal);

			//for debugging - force a bitrate value to override the calculated one
			if(_forceBitrate > 0)
			{
				bitrateVal = _forceBitrate;
			}

			sendNotification(NotificationType.CHANGE_PREFERRED_BITRATE, {bitrate: bitrateVal});

			_bandwidth = bitrateVal;

			//write to cookie
			if (_configProxy.vo.flashvars.allowCookies=="true")
			{
				var flavorCookie : SharedObject;
				try
				{
					flavorCookie = SharedObject.getLocal("kaltura");
				}
				catch (e : Error)
				{
					trace("No access to user's file system");
				}
				if (flavorCookie && flavorCookie.data)
				{
					flavorCookie.data.preferedFlavorBR = bitrateVal;
					flavorCookie.flush();
				}
			}

			finishDownloadProcess();
		}

		/**
		 * default timer time has passed, stop listening to progress event and continue the flow
		 * @param event
		 *
		 */
		private function onTimerComplete (event:TimerEvent):void {
			downloadComplete();
		}

		/**
		 * I/O error getting the sample file, release the UI
		 * @param event
		 *
		 */
		private function ioErrorHandler(event:IOErrorEvent) : void
		{
			//Bypass: ignore #2124 error (loaded file is an unknown type)
			if (!event.text || event.text.indexOf("Error #2124")==-1)
			{
				trace ("bitrate detection i/o error:", event.text);
				finishDownloadProcess();
			}
		}

		/**
		 * enable back the GUI.
		 * call Do_Play if we were in auto play and set back relevant flashvars
		 *
		 */
		private function finishDownloadProcess():void {
			//if we changed variables, set them back
			if (_wasAutoPlay || _wasSingleAutoPlay) {
				var mediaProxy:MediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
				var sequenceProxy:SequenceProxy = facade.retrieveProxy(SequenceProxy.NAME) as SequenceProxy;
				if (_wasAutoPlay)
				{
					_configProxy.vo.flashvars.autoPlay = "true";
				}
				else
				{
					mediaProxy.vo.singleAutoPlay = true;
				}
				//play if we should have played
				if (!_playedPlayed && !mediaProxy.vo.isMediaDisabled && !sequenceProxy.vo.isInSequence)
				{
					sendNotification(NotificationType.DO_PLAY);
				}
			}

			sendNotification(NotificationType.ENABLE_GUI, {guiEnabled: true, enableType: EnableType.FULL});
		}

	}

}

remove debugging code

qnd: remove debugging code

git-svn-id: 3f608e5a9a704dd448217c0a64c508e7f145cfa1@87286 6b8eccd3-e8c5-4e7d-8186-e12b5326b719


////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package org.apache.flex.charts.beads
{
	import org.apache.flex.charts.core.IChart;
	import org.apache.flex.charts.core.IChartItemRenderer;
	import org.apache.flex.charts.core.IChartSeries;
	import org.apache.flex.core.IBead;
	import org.apache.flex.core.IDataProviderItemRendererMapper;
	import org.apache.flex.core.IItemRendererClassFactory;
	import org.apache.flex.core.IItemRendererParent;
	import org.apache.flex.core.ISelectionModel;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.events.Event;
	import org.apache.flex.events.IEventDispatcher;
	import org.apache.flex.html.beads.IListView;

	/**
	 *  The ChartItemRendererFactory class implements IDataProviderItemRendererMapper
	 *  and creats the itemRenderers for each series in a chart. The itemRenderer class
	 *  is identified on each series either through a property or through a CSS style.
	 *  Once all of the itemRenderers are created, an itemsCreated event is dispatched
	 *  causing the layout associated with the chart to size and position the items.
	 *
	 *  @langversion 3.0
	 *  @playerversion Flash 10.2
	 *  @playerversion AIR 2.6
	 *  @productversion FlexJS 0.0
	 */
	public class ChartItemRendererFactory implements IBead, IDataProviderItemRendererMapper
	{
		/**
		 *  constructor.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public function ChartItemRendererFactory()
		{
		}

		private var selectionModel:ISelectionModel;
		protected var dataGroup:IItemRendererParent;

		private var _seriesRenderers:Array;

		/**
		 *  The array of renderers created for each series.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public function get seriesRenderers():Array
		{
			return _seriesRenderers;
		}

		private var _strand:IStrand;

		/**
		 *  @copy org.apache.flex.core.IBead#strand
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public function set strand(value:IStrand):void
		{
			_strand = value;
			selectionModel = value.getBeadByType(ISelectionModel) as ISelectionModel;
			var listView:IListView = value.getBeadByType(IListView) as IListView;
			dataGroup = listView.dataGroup;
			//			selectionModel.addEventListener("dataProviderChanged", dataProviderChangeHandler);

			var dp:Array = selectionModel.dataProvider as Array;
			if (!dp)
				return;

			_seriesRenderers = new Array();

			dataGroup.removeAllElements();

			var series:Array = IChart(_strand).series;

			for( var j:int=0; j < dp.length; j++)
			{
				var renderers:Array = new Array();

				for( var i:int=0; i < series.length; i++)
				{
					var s:IChartSeries = series[i] as IChartSeries;
					var k:IChartItemRenderer = s.itemRenderer.newInstance() as IChartItemRenderer;
					k.itemRendererParent = dataGroup;
					k.xField = s.xField;
					k.yField = s.yField;
					k.fillColor = s.fillColor;
					k.data = dp[j];
					k.index = j;

					renderers.push(k);

					dataGroup.addElement(k);
				}

				_seriesRenderers.push(renderers);
			}

			IEventDispatcher(_strand).dispatchEvent(new Event("itemsCreated"));
		}

		/**
		 * @private
		 */
		public function get itemRendererFactory():IItemRendererClassFactory
		{
			return null;
		}

		/**
		 * @private
		 */
		public function set itemRendererFactory(value:IItemRendererClassFactory):void
		{
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package org.apache.flex.charts.beads
{
	import org.apache.flex.charts.core.IChart;
	import org.apache.flex.charts.core.IChartItemRenderer;
	import org.apache.flex.charts.core.IChartSeries;
	import org.apache.flex.core.IBead;
	import org.apache.flex.core.IDataProviderItemRendererMapper;
	import org.apache.flex.core.IItemRendererClassFactory;
	import org.apache.flex.core.IItemRendererParent;
	import org.apache.flex.core.ISelectionModel;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.events.Event;
	import org.apache.flex.events.IEventDispatcher;
	import org.apache.flex.html.beads.IListView;

	/**
	 *  The ChartItemRendererFactory class implements IDataProviderItemRendererMapper
	 *  and creats the itemRenderers for each series in a chart. The itemRenderer class
	 *  is identified on each series either through a property or through a CSS style.
	 *  Once all of the itemRenderers are created, an itemsCreated event is dispatched
	 *  causing the layout associated with the chart to size and position the items.
	 *
	 *  @langversion 3.0
	 *  @playerversion Flash 10.2
	 *  @playerversion AIR 2.6
	 *  @productversion FlexJS 0.0
	 */
	public class ChartItemRendererFactory implements IBead, IDataProviderItemRendererMapper
	{
		/**
		 *  constructor.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public function ChartItemRendererFactory()
		{
		}

		private var selectionModel:ISelectionModel;
		protected var dataGroup:IItemRendererParent;

		private var _seriesRenderers:Array;

		/**
		 *  The array of renderers created for each series.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public function get seriesRenderers():Array
		{
			return _seriesRenderers;
		}

		private var _strand:IStrand;

		/**
		 *  @copy org.apache.flex.core.IBead#strand
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public function set strand(value:IStrand):void
		{
			_strand = value;
			selectionModel = value.getBeadByType(ISelectionModel) as ISelectionModel;
			var listView:IListView = value.getBeadByType(IListView) as IListView;
			dataGroup = listView.dataGroup;
			//			selectionModel.addEventListener("dataProviderChanged", dataProviderChangeHandler);

			var dp:Array = selectionModel.dataProvider as Array;
			if (!dp)
				return;

			_seriesRenderers = new Array();

			dataGroup.removeAllElements();

			var series:Array = IChart(_strand).series;

			for( var j:int=0; j < dp.length; j++)
			{
				var renderers:Array = new Array();

				for( var i:int=0; i < series.length; i++)
				{
					var s:IChartSeries = series[i] as IChartSeries;
					var k:IChartItemRenderer = s.itemRenderer.newInstance() as IChartItemRenderer;
					k.itemRendererParent = dataGroup;
					k.xField = s.xField;
					k.yField = s.yField;
					//k.fillColor = s.fillColor;
					k.data = dp[j];
					k.index = j;

					renderers.push(k);

					dataGroup.addElement(k);
				}

				_seriesRenderers.push(renderers);
			}

			IEventDispatcher(_strand).dispatchEvent(new Event("itemsCreated"));
		}

		/**
		 * @private
		 */
		public function get itemRendererFactory():IItemRendererClassFactory
		{
			return null;
		}

		/**
		 * @private
		 */
		public function set itemRendererFactory(value:IItemRendererClassFactory):void
		{
		}
	}
}

Remove reference to IChartItemRenderer.fillColor as that property does not exist. This fixes the FlexJSJX build.


package com.mangui.HLS.muxing {

	import com.mangui.HLS.muxing.*;
	import com.mangui.HLS.utils.Log;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.utils.ByteArray;
	import flash.utils.Timer;

	/** Representation of an MPEG transport stream. **/
	public class TS extends EventDispatcher {

		/** TS Sync byte. **/
		public static const SYNCBYTE:uint = 0x47;
		/** TS Packet size in byte. **/
		public static const PACKETSIZE:uint = 188;
		/** Identifier for read complete event **/
		public static const READCOMPLETE:String = "readComplete"
		private static const COUNT:uint = 5000;

		/** Packet ID of the AAC audio stream. **/
		private var _aacId:Number = -1;
		/** List with audio frames. **/
		public var audioTags:Vector.<Tag> = new Vector.<Tag>();
		/** List of packetized elementary streams with AAC. **/
		private var _audioPES:Vector.<PES> = new Vector.<PES>();
		/** Packet ID of the video stream. **/
		private var _avcId:Number = -1;
		/** PES packet that contains the first keyframe. **/
		private var _firstKey:Number = -1;
		/** Packet ID of the MP3 audio stream. **/
		private var _mp3Id:Number = -1;
		/** Packet ID of the PAT (is always 0). **/
		private var _patId:Number = 0;
		/** Packet ID of the Program Map Table. **/
		private var _pmtId:Number = -1;
		/** List with video frames. **/
		/** Packet ID of the SDT (is always 17). **/
		private var _sdtId:Number = 17;
		public var videoTags:Vector.<Tag> = new Vector.<Tag>();
		/** List of packetized elementary streams with AVC. **/
		private var _videoPES:Vector.<PES> = new Vector.<PES>();
		/** Timer for reading packets **/
		public var _timer:Timer;
		/** Byte data to be read **/
		private var _data:ByteArray;

		/** Transmux the M2TS file into an FLV file. **/
		public function TS(data:ByteArray) {
			// Extract the elementary streams.
			_data = data;
			_timer = new Timer(0,0);
			_timer.addEventListener(TimerEvent.TIMER, _readData);
		};

		/** Read a small chunk of packets each time to avoid blocking **/
		private function _readData(e:Event):void {
			var i:uint = 0;
			while(_data.bytesAvailable && i < COUNT) {
				_readPacket();
				i++;

			}
			if (!_data.bytesAvailable) {
				_timer.stop();
				_extractFrames();
			}
		}

		/** start the timer in order to start reading data **/
		public function startReading():void {;
			_timer.start();
		}

		/** setup the video and audio tag vectors from the read data **/
		private function _extractFrames():void {
			if (_videoPES.length == 0 && _audioPES.length == 0 ) {
				throw new Error("No AAC audio and no AVC video stream found.");
			}
			// Extract the ADTS or MPEG audio frames.
			if(_aacId > 0) {
				_readADTS();
			} else {
				_readMPEG();
			}
			// Extract the NALU video frames.
			_readNALU();
			dispatchEvent(new Event(TS.READCOMPLETE));
		}

		/** Get audio configuration data. **/
		public function getADIF():ByteArray {
			if(_aacId > 0 && audioTags.length > 0) {
				return AAC.getADIF(_audioPES[0].data,_audioPES[0].payload);
			} else {
				return new ByteArray();
			}
		};

		/** Get video configuration data. **/
		public function getAVCC():ByteArray {
			if(_firstKey == -1) {
				return new ByteArray();
			}
			return AVC.getAVCC(_videoPES[_firstKey].data,_videoPES[_firstKey].payload);
		};

		/** Read ADTS frames from audio PES streams. **/
		private function _readADTS():void {
			var frames:Array;
			var overflow:Number = 0;
			var tag:Tag;
			var stamp:Number;
			for(var i:Number=0; i<_audioPES.length; i++) {
				// Parse the PES headers.
				_audioPES[i].parse();
				// Correct for Segmenter's "optimize", which cuts frames in half.
				if(overflow > 0) {
					_audioPES[i-1].data.position = _audioPES[i-1].data.length;
					_audioPES[i-1].data.writeBytes(_audioPES[i].data,_audioPES[i].payload,overflow);
					_audioPES[i].payload += overflow;
				}
				// Store ADTS frames in array.
				frames = AAC.getFrames(_audioPES[i].data,_audioPES[i].payload);
				for(var j:Number=0; j< frames.length; j++) {
					// Increment the timestamp of subsequent frames.
					stamp = Math.round(_audioPES[i].pts + j * 1024 * 1000 / frames[j].rate);
					tag = new Tag(Tag.AAC_RAW, stamp, stamp, false);
					if(i == _audioPES.length-1 && j == frames.length - 1) {
					  if((_audioPES[i].data.length - frames[j].start)>0) {
						  tag.push(_audioPES[i].data, frames[j].start, _audioPES[i].data.length - frames[j].start);
					  }
					} else {
						tag.push(_audioPES[i].data, frames[j].start, frames[j].length);
					}
					audioTags.push(tag);
				}
				// Correct for Segmenter's "optimize", which cuts frames in half.
				overflow = frames[frames.length-1].start +
					frames[frames.length-1].length - _audioPES[i].data.length;
			}
		};

		/** Read MPEG data from audio PES streams. **/
		private function _readMPEG():void {
			var tag:Tag;
			for(var i:Number=0; i<_audioPES.length; i++) {
				_audioPES[i].parse();
				tag = new Tag(Tag.MP3_RAW, _audioPES[i].pts,_audioPES[i].dts, false);
				tag.push(_audioPES[i].data, _audioPES[i].payload, _audioPES[i].data.length-_audioPES[i].payload);
				audioTags.push(tag);
			}
		};

		/** Read NALU frames from video PES streams. **/
		private function _readNALU():void {
			var overflow:Number;
			var units:Array;
			var last:Number;
			for(var i:Number=0; i<_videoPES.length; i++) {
				// Parse the PES headers and NAL units.
				try {
					_videoPES[i].parse();
				} catch (error:Error) {
					Log.txt(error.message);
					continue;
				}
				units = AVC.getNALU(_videoPES[i].data,_videoPES[i].payload);
				// If there's no NAL unit, push all data in the previous tag.
				if(!units.length) {
					videoTags[videoTags.length-1].push(_videoPES[i].data, _videoPES[i].payload,
						_videoPES[i].data.length - _videoPES[i].payload);
					continue;
				}
				// If NAL units are offset, push preceding data into the previous tag.
				overflow = units[0].start - units[0].header - _videoPES[i].payload;
				if(overflow) {
					videoTags[videoTags.length-1].push(_videoPES[i].data,_videoPES[i].payload,overflow);
				}
				videoTags.push(new Tag(Tag.AVC_NALU,_videoPES[i].pts,_videoPES[i].dts,false));
				// Only push NAL units 1 to 5 into tag.
				for(var j:Number = 0; j < units.length; j++) {
					if (units[j].type < 6) {
						videoTags[videoTags.length-1].push(_videoPES[i].data,units[j].start,units[j].length);
						// Unit type 5 indicates a keyframe.
						if(units[j].type == 5) {
							videoTags[videoTags.length-1].keyframe = true;
							if(_firstKey == -1) {
								_firstKey = i;
							}
						}
					}
				}
			}
		};

		/** Read TS packet. **/
		private function _readPacket():void {
			// Each packet is 188 bytes.
			var todo:uint = TS.PACKETSIZE;
			// Sync byte.
			if(_data.readByte() != TS.SYNCBYTE) {
				throw new Error("Could not parse TS file: sync byte not found.");
			}
			todo--;
			// Payload unit start indicator.
			var stt:uint = (_data.readUnsignedByte() & 64) >> 6;
			_data.position--;

			// Packet ID (last 13 bits of UI16).
			var pid:uint = _data.readUnsignedShort() & 8191;
			// Check for adaptation field.
			todo -=2;
			var atf:uint = (_data.readByte() & 48) >> 4;
			todo --;
			// Read adaptation field if available.
			if(atf > 1) {
				// Length of adaptation field.
				var len:uint = _data.readUnsignedByte();
				todo--;
				// Random access indicator (keyframe).
				//var rai:uint = data.readUnsignedByte() & 64;
				_data.position += len;
				todo -= len;
				// Return if there's only adaptation field.
				if(atf == 2 || len == 183) {
					_data.position += todo;
					return;
				}
			}

			var pes:ByteArray = new ByteArray();
			// Parse the PES, split by Packet ID.
			switch (pid) {
				case _patId:
					todo -= _readPAT();
					break;
				case _pmtId:
					todo -= _readPMT();
					break;
				case _aacId:
				case _mp3Id:
					if(stt) {
						pes.writeBytes(_data,_data.position,todo);
						_audioPES.push(new PES(pes,true));
					} else if (_audioPES.length) {
						_audioPES[_audioPES.length-1].data.writeBytes(_data,_data.position,todo);
					} else {
						Log.txt("Discarding TS audio packet with id "+pid);
					}
					break;
				case _avcId:
					if(stt) {
						pes.writeBytes(_data,_data.position,todo);
						_videoPES.push(new PES(pes,false));
					} else if (_videoPES.length) {
						_videoPES[_videoPES.length-1].data.writeBytes(_data,_data.position,todo);
					} else {
						Log.txt("Discarding TS video packet with id "+pid);
					}
					break;
				case _sdtId:
						break;
				default:
					// Ignored other packet IDs, only report error if PMT not yet parsed
					// this helps to detect discarded TS packets because of PAT/PMT not being at beginning of TS fragment
					if(_pmtId == -1)
					   Log.txt("Discarding unassignable TS packets with id "+pid);
					break;
			}
			// Jump to the next packet.
			_data.position += todo;
		};

		/** Read the Program Association Table. **/
		private function _readPAT():Number {
			// Check the section length for a single PMT.
			_data.position += 3;
			if(_data.readUnsignedByte() > 13) {
				throw new Error("Multiple PMT/NIT entries are not supported.");
			}
			// Grab the PMT ID.
			_data.position += 7;
			_pmtId = _data.readUnsignedShort() & 8191;
			return 13;
		};

		/** Read the Program Map Table. **/
		private function _readPMT():Number {
			// Check the section length for a single PMT.
			_data.position += 3;
			var len:uint = _data.readByte();
			var read:uint = 13;
			_data.position += 8;
			var pil:Number = _data.readByte();
			_data.position += pil;
			read += pil;
			// Loop through the streams in the PMT.
			while(read < len) {
				var typ:uint = _data.readByte();
				var sid:uint = _data.readUnsignedShort() & 8191;
				if(typ == 0x0F) {
					_aacId = sid;
				} else if (typ == 0x1B) {
					_avcId = sid;
				} else if (typ == 0x03 || typ == 0x04) {
					_mp3Id = sid;
				}
				//  descriptor loop length
				_data.position++;
				var sel:uint = _data.readByte() & 0x3F;
				_data.position += sel;
				read += sel + 5;
			}
			return len;
		};

	}

}

package com.mangui.HLS.muxing {

	import com.mangui.HLS.muxing.*;
	import com.mangui.HLS.utils.Log;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.utils.ByteArray;
	import flash.utils.Timer;

	/** Representation of an MPEG transport stream. **/
	public class TS extends EventDispatcher {

		/** TS Sync byte. **/
		public static const SYNCBYTE:uint = 0x47;
		/** TS Packet size in byte. **/
		public static const PACKETSIZE:uint = 188;
		/** Identifier for read complete event **/
		public static const READCOMPLETE:String = "readComplete"
		private static const COUNT:uint = 5000;

		/** Packet ID of the AAC audio stream. **/
		private var _aacId:Number = -1;
		/** List with audio frames. **/
		public var audioTags:Vector.<Tag> = new Vector.<Tag>();
		/** List of packetized elementary streams with AAC. **/
		private var _audioPES:Vector.<PES> = new Vector.<PES>();
		/** has PMT been parsed ? **/
		private var _pmtParsed:Boolean= false;
		/** nb of AV packets before PMT **/
		private var _AVpacketsBeforePMT:Number = 0;
		/** Packet ID of the video stream. **/
		private var _avcId:Number = -1;
		/** PES packet that contains the first keyframe. **/
		private var _firstKey:Number = -1;
		/** Packet ID of the MP3 audio stream. **/
		private var _mp3Id:Number = -1;
		/** Packet ID of the PAT (is always 0). **/
		private var _patId:Number = 0;
		/** Packet ID of the Program Map Table. **/
		private var _pmtId:Number = -1;
		/** List with video frames. **/
		/** Packet ID of the SDT (is always 17). **/
		private var _sdtId:Number = 17;
		public var videoTags:Vector.<Tag> = new Vector.<Tag>();
		/** List of packetized elementary streams with AVC. **/
		private var _videoPES:Vector.<PES> = new Vector.<PES>();
		/** Timer for reading packets **/
		public var _timer:Timer;
		/** Byte data to be read **/
		private var _data:ByteArray;
		/* last key Frame PES packet */
		private var _lastKeyFrame:PES = null;

		/** Transmux the M2TS file into an FLV file. **/
		public function TS(data:ByteArray) {
			// Extract the elementary streams.
			_data = data;
			_timer = new Timer(0,0);
			_timer.addEventListener(TimerEvent.TIMER, _readData);
		};

		/** add new data into Buffer */
		public function addData(newData:ByteArray):void {
		  newData.readBytes(_data,_data.position);
		  _timer.start();
		}

		/** Read a small chunk of packets each time to avoid blocking **/
		private function _readData(e:Event):void {
			var i:uint = 0;
			while(_data.bytesAvailable && i < COUNT) {
				_readPacket();
				i++;

			}
			if (!_data.bytesAvailable) {
				_timer.stop();
				_extractFrames();
			}
		}

		/** start the timer in order to start reading data **/
		public function startReading():void {;
			_timer.start();
		}

		/** setup the video and audio tag vectors from the read data **/
		private function _extractFrames():void {
			if (_videoPES.length == 0 && _audioPES.length == 0 ) {
				throw new Error("No AAC audio and no AVC video stream found.");
			}
			// Extract the ADTS or MPEG audio frames.
			if(_aacId > 0) {
				_readADTS();
			} else {
				_readMPEG();
			}
			// Extract the NALU video frames.
			_readNALU();
			dispatchEvent(new Event(TS.READCOMPLETE));
		}

		/** Get audio configuration data. **/
		public function getADIF():ByteArray {
			if(_aacId > 0 && audioTags.length > 0) {
				return AAC.getADIF(_audioPES[0].data,_audioPES[0].payload);
			} else {
				return new ByteArray();
			}
		};

		/** Get video configuration data. **/
		public function getAVCC():ByteArray {
			if(_firstKey == -1) {
				return new ByteArray();
			}
			return AVC.getAVCC(_lastKeyFrame.data,_lastKeyFrame.payload);
		};

		/** Read ADTS frames from audio PES streams. **/
		private function _readADTS():void {
			var frames:Array;
			var overflow:Number = 0;
			var tag:Tag;
			var stamp:Number;
			for(var i:Number=0; i<_audioPES.length; i++) {
				// Parse the PES headers.
				_audioPES[i].parse();
				// Correct for Segmenter's "optimize", which cuts frames in half.
				if(overflow > 0) {
					_audioPES[i-1].data.position = _audioPES[i-1].data.length;
					_audioPES[i-1].data.writeBytes(_audioPES[i].data,_audioPES[i].payload,overflow);
					_audioPES[i].payload += overflow;
				}
				// Store ADTS frames in array.
				frames = AAC.getFrames(_audioPES[i].data,_audioPES[i].payload);
				for(var j:Number=0; j< frames.length; j++) {
					// Increment the timestamp of subsequent frames.
					stamp = Math.round(_audioPES[i].pts + j * 1024 * 1000 / frames[j].rate);
					tag = new Tag(Tag.AAC_RAW, stamp, stamp, false);
					if(i == _audioPES.length-1 && j == frames.length - 1) {
					  if((_audioPES[i].data.length - frames[j].start)>0) {
						  tag.push(_audioPES[i].data, frames[j].start, _audioPES[i].data.length - frames[j].start);
					  }
					} else {
						tag.push(_audioPES[i].data, frames[j].start, frames[j].length);
					}
					audioTags.push(tag);
				}
				// Correct for Segmenter's "optimize", which cuts frames in half.
				overflow = frames[frames.length-1].start +
					frames[frames.length-1].length - _audioPES[i].data.length;
			}
		};

		/** Read MPEG data from audio PES streams. **/
		private function _readMPEG():void {
			var tag:Tag;
			for(var i:Number=0; i<_audioPES.length; i++) {
				_audioPES[i].parse();
				tag = new Tag(Tag.MP3_RAW, _audioPES[i].pts,_audioPES[i].dts, false);
				tag.push(_audioPES[i].data, _audioPES[i].payload, _audioPES[i].data.length-_audioPES[i].payload);
				audioTags.push(tag);
			}
		};

		/** Read NALU frames from video PES streams. **/
		private function _readNALU():void {
			var overflow:Number;
			var units:Array;
			var last:Number;
			for(var i:Number=0; i<_videoPES.length; i++) {
				// Parse the PES headers and NAL units.
				try {
					_videoPES[i].parse();
				} catch (error:Error) {
					Log.txt(error.message);
					continue;
				}
				units = AVC.getNALU(_videoPES[i].data,_videoPES[i].payload);
				// If there's no NAL unit, push all data in the previous tag.
				if(!units.length) {
					videoTags[videoTags.length-1].push(_videoPES[i].data, _videoPES[i].payload,
						_videoPES[i].data.length - _videoPES[i].payload);
					continue;
				}
				// If NAL units are offset, push preceding data into the previous tag.
				overflow = units[0].start - units[0].header - _videoPES[i].payload;
				if(overflow) {
					videoTags[videoTags.length-1].push(_videoPES[i].data,_videoPES[i].payload,overflow);
				}
				videoTags.push(new Tag(Tag.AVC_NALU,_videoPES[i].pts,_videoPES[i].dts,false));
				// Only push NAL units 1 to 5 into tag.
				for(var j:Number = 0; j < units.length; j++) {
					if (units[j].type < 6) {
						videoTags[videoTags.length-1].push(_videoPES[i].data,units[j].start,units[j].length);
						// Unit type 5 indicates a keyframe.
						if(units[j].type == 5) {
							videoTags[videoTags.length-1].keyframe = true;
							if(_firstKey == -1) {
								_firstKey = i;
								_lastKeyFrame=_videoPES[i];
							}
						}
					}
				}
			}
		};

		/** Read TS packet. **/
		private function _readPacket():void {
			// Each packet is 188 bytes.
			var todo:uint = TS.PACKETSIZE;
			// Sync byte.
			if(_data.readByte() != TS.SYNCBYTE) {
				throw new Error("Could not parse TS file: sync byte not found.");
			}
			todo--;
			// Payload unit start indicator.
			var stt:uint = (_data.readUnsignedByte() & 64) >> 6;
			_data.position--;

			// Packet ID (last 13 bits of UI16).
			var pid:uint = _data.readUnsignedShort() & 8191;
			// Check for adaptation field.
			todo -=2;
			var atf:uint = (_data.readByte() & 48) >> 4;
			todo --;
			// Read adaptation field if available.
			if(atf > 1) {
				// Length of adaptation field.
				var len:uint = _data.readUnsignedByte();
				todo--;
				// Random access indicator (keyframe).
				//var rai:uint = data.readUnsignedByte() & 64;
				_data.position += len;
				todo -= len;
				// Return if there's only adaptation field.
				if(atf == 2 || len == 183) {
					_data.position += todo;
					return;
				}
			}

			var pes:ByteArray = new ByteArray();
			// Parse the PES, split by Packet ID.
			switch (pid) {
				case _patId:
					todo -= _readPAT();
					break;
				case _pmtId:
					todo -= _readPMT();
					break;
				case _aacId:
				case _mp3Id:
					if(stt) {
						pes.writeBytes(_data,_data.position,todo);
						_audioPES.push(new PES(pes,true));
					} else if (_audioPES.length) {
						_audioPES[_audioPES.length-1].data.writeBytes(_data,_data.position,todo);
					} else {
						Log.txt("Discarding TS audio packet with id "+pid);
					}
					break;
				case _avcId:
					if(stt) {
						pes.writeBytes(_data,_data.position,todo);
						_videoPES.push(new PES(pes,false));
					} else if (_videoPES.length) {
						_videoPES[_videoPES.length-1].data.writeBytes(_data,_data.position,todo);
					} else {
						Log.txt("Discarding TS video packet with id "+pid + " bad TS segmentation ?");
					}
					break;
				case _sdtId:
						break;
				default:
				_AVpacketsBeforePMT++;
					break;
			}
			// Jump to the next packet.
			_data.position += todo;
		};

		/** Read the Program Association Table. **/
		private function _readPAT():Number {
			// Check the section length for a single PMT.
			_data.position += 3;
			if(_data.readUnsignedByte() > 13) {
				throw new Error("Multiple PMT entries are not supported.");
			}
			// Grab the PMT ID.
			_data.position += 7;
			_pmtId = _data.readUnsignedShort() & 8191;
			return 13;
		};

		/** Read the Program Map Table. **/
		private function _readPMT():Number {
			// Check the section length for a single PMT.
			_data.position += 3;
			var len:uint = _data.readByte();
			var read:uint = 13;
			_data.position += 8;
			var pil:Number = _data.readByte();
			_data.position += pil;
			read += pil;
			// Loop through the streams in the PMT.
			while(read < len) {
				var typ:uint = _data.readByte();
				var sid:uint = _data.readUnsignedShort() & 8191;
				if(typ == 0x0F) {
					_aacId = sid;
				} else if (typ == 0x1B) {
					_avcId = sid;
				} else if (typ == 0x03 || typ == 0x04) {
					_mp3Id = sid;
				}
				//  descriptor loop length
				_data.position++;
				var sel:uint = _data.readByte() & 0x3F;
				_data.position += sel;
				read += sel + 5;
			}
			if(_pmtParsed == false) {
			  _pmtParsed = true;
			// if PMT was not parsed before, and some unknown packets have been skipped in between, rewind to beginning of the stream
			// it helps with fragment not segmented properly (in theory there should be no A/V packets before PAT/PMT)
			  if (_AVpacketsBeforePMT > 1) {
			    Log.txt("late PMT found, rewinding at beginning of TS");
			    return (-_data.position);
			  }
		  }
			return len;
		};
	}
}

enhance TS parsing if PMT not at the beginning of the fragment and some AV packets were left before, rewind and parse those AV packets. this should help parsing bad segmented files


package org.jivesoftware.xiff.data{
	/*
	 * Copyright (C) 2003-2007
	 * Nick Velloff <nick.velloff@gmail.com>
	 * Derrick Grigg <dgrigg@rogers.com>
	 * Sean Voisen <sean@voisen.org>
	 * Sean Treadway <seant@oncotype.dk>
	 *
	 * This library is free software; you can redistribute it and/or
	 * modify it under the terms of the GNU Lesser General Public
	 * License as published by the Free Software Foundation; either
	 * version 2.1 of the License, or (at your option) any later version.
	 *
	 * This library is distributed in the hope that it will be useful,
	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	 * Lesser General Public License for more details.
	 *
	 * You should have received a copy of the GNU Lesser General Public
	 * License along with this library; if not, write to the Free Software
	 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
	 *
	 */

	import org.jivesoftware.xiff.data.ISerializable;
	import org.jivesoftware.xiff.data.XMPPStanza;
	import flash.xml.XMLNode;

	/**
	 * This class provides encapsulation for manipulation of presence data for sending and receiving.
	 *
	 * @author Sean Voisen
	 * @since 2.0.0
	 * @availability Flash Player 7
	 * @param recipient The recipient of the presence, usually in the form of a JID.
	 * @param sender The sender of the presence, usually in the form of a JID.
	 * @param presenceType The type of presence as a string. There are predefined static variables for this.
	 * @param showVal What to show for this presence (away, online, etc.) There are predefined static variables for this.
	 * @param statusVal The status; usually used for the "away message."
	 * @param priorityVal The priority of this presence; usually on a scale of 1-5.
	 * @toc-path Data
	 * @toc-sort 1
	 */
	public class Presence extends XMPPStanza implements ISerializable
	{
		// Static variables for specific type strings
		public static var AVAILABLE_TYPE:String = "available";
		public static var UNAVAILABLE_TYPE:String = "unavailable";
		public static var PROBE_TYPE:String = "probe";
		public static var SUBSCRIBE_TYPE:String = "subscribe";
		public static var UNSUBSCRIBE_TYPE:String = "unsubscribe";
		public static var SUBSCRIBED_TYPE:String = "subscribed";
		public static var UNSUBSCRIBED_TYPE:String = "unsubscribed";
		public static var ERROR_TYPE:String = "error";

		// Static variables for show values
		public static var SHOW_AWAY:String = "away";
		public static var SHOW_CHAT:String = "chat";
		public static var SHOW_DND:String = "dnd";
		public static var SHOW_NORMAL:String = "normal";
		public static var SHOW_XA:String = "xa";
		public static var SHOW_OFFLINE:String = "offline";

		// Private node references for property lookups
		private var myShowNode:XMLNode;
		private var myStatusNode:XMLNode;
		private var myPriorityNode:XMLNode;

		public function Presence( recipient:String=null, sender:String=null, presenceType:String=null, showVal:String=null, statusVal:String=null, priorityVal:Number=0 )
		{
			super( recipient, sender, presenceType, null, "presence" );

			show = showVal;
			status = statusVal;
			priority = priorityVal;
		}

		/**
		 * Serializes the Presence into XML form for sending to a server.
		 *
		 * @return An indication as to whether serialization was successful
		 * @availability Flash Player 7
		 */
		override public function serialize( parentNode:XMLNode ):Boolean
		{
			return super.serialize( parentNode );
		}

		/**
		 * Deserializes an XML object and populates the Presence instance with its data.
		 *
		 * @param xmlNode The XML to deserialize
		 * @return An indication as to whether deserialization was sucessful
		 * @availability Flash Player 7
		 */
		override public function deserialize( xmlNode:XMLNode ):Boolean
		{
			var isDeserialized:Boolean = super.deserialize( xmlNode );

			if (isDeserialized) {
				var children:Array = xmlNode.childNodes;
				for( var i:String in children )
				{
					switch( children[i].nodeName )
					{
						case "show":
							myShowNode = children[i];
							break;

						case "status":
							myStatusNode = children[i];
							break;

						case "priority":
							myPriorityNode = children[i];
							break;
					}
				}
			}
			return isDeserialized;
		}

		/**
		 * The show value; away, online, etc. There are predefined static variables in the Presence
		 * class for this:
		 *

		 *
Presence.SHOW_AWAY

		 *
Presence.SHOW_CHAT

		 *
Presence.SHOW_DND

		 *
Presence.SHOW_NORMAL

		 *
Presence.SHOW_XA

		 *

		 *
		 * @availability Flash Player 7
		 */
		public function get show():String
		{
			if (myShowNode == null) return null;

			if (!exists(myShowNode.firstChild)){
				return Presence.SHOW_NORMAL;
			}
			return myShowNode.firstChild.nodeValue;
		}

		public function set show( showVal:String ):void
		{

			myShowNode = replaceTextNode(getNode(), myShowNode, "show", showVal);
		}

		/**
		 * The status; usually used for "away messages."
		 *
		 * @availability Flash Player 7
		 */
		public function get status():String  {
			if (myStatusNode == null || myStatusNode.firstChild == null) return null;
			return myStatusNode.firstChild.nodeValue;
		}

		public function set status( statusVal:String ):void
		{
			myStatusNode = replaceTextNode(getNode(), myStatusNode, "status", statusVal);
		}

		/**
		 * The priority of the presence, usually on a scale of 1-5.
		 *
		 * @availability Flash Player 7
		 */
		public function get priority():Number
		{
			if (myPriorityNode == null) return NaN;
			var p:Number = Number(myPriorityNode.firstChild.nodeValue);
			if( isNaN( p ) ) {
				return NaN;
			}
			else {
				return p;
			}
		}

		public function set priority( priorityVal:Number ):void
		{
			myPriorityNode = replaceTextNode(getNode(), myPriorityNode, "priority", priorityVal.toString());
		}
	}
}

package org.jivesoftware.xiff.data{
	/*
	 * Copyright (C) 2003-2007
	 * Nick Velloff <nick.velloff@gmail.com>
	 * Derrick Grigg <dgrigg@rogers.com>
	 * Sean Voisen <sean@voisen.org>
	 * Sean Treadway <seant@oncotype.dk>
	 *
	 * This library is free software; you can redistribute it and/or
	 * modify it under the terms of the GNU Lesser General Public
	 * License as published by the Free Software Foundation; either
	 * version 2.1 of the License, or (at your option) any later version.
	 *
	 * This library is distributed in the hope that it will be useful,
	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	 * Lesser General Public License for more details.
	 *
	 * You should have received a copy of the GNU Lesser General Public
	 * License along with this library; if not, write to the Free Software
	 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
	 *
	 */

	import org.jivesoftware.xiff.data.ISerializable;
	import org.jivesoftware.xiff.data.XMPPStanza;
	import flash.xml.XMLNode;

	/**
	 * This class provides encapsulation for manipulation of presence data for sending and receiving.
	 *
	 * @author Sean Voisen
	 * @since 2.0.0
	 * @availability Flash Player 7
	 * @param recipient The recipient of the presence, usually in the form of a JID.
	 * @param sender The sender of the presence, usually in the form of a JID.
	 * @param presenceType The type of presence as a string. There are predefined static variables for this.
	 * @param showVal What to show for this presence (away, online, etc.) There are predefined static variables for this.
	 * @param statusVal The status; usually used for the "away message."
	 * @param priorityVal The priority of this presence; usually on a scale of 1-5.
	 * @toc-path Data
	 * @toc-sort 1
	 */
	public class Presence extends XMPPStanza implements ISerializable
	{
		// Static constants for specific type strings
		public static const AVAILABLE_TYPE:String = "available";
		public static const UNAVAILABLE_TYPE:String = "unavailable";
		public static const PROBE_TYPE:String = "probe";
		public static const SUBSCRIBE_TYPE:String = "subscribe";
		public static const UNSUBSCRIBE_TYPE:String = "unsubscribe";
		public static const SUBSCRIBED_TYPE:String = "subscribed";
		public static const UNSUBSCRIBED_TYPE:String = "unsubscribed";
		public static const ERROR_TYPE:String = "error";

		// Static constants for show values
		public static const SHOW_AWAY:String = "away";
		public static const SHOW_CHAT:String = "chat";
		public static const SHOW_DND:String = "dnd";
		public static const SHOW_NORMAL:String = "normal";
		public static const SHOW_XA:String = "xa";
		public static const SHOW_OFFLINE:String = "offline";

		// Private node references for property lookups
		private var myShowNode:XMLNode;
		private var myStatusNode:XMLNode;
		private var myPriorityNode:XMLNode;

		public function Presence( recipient:String=null, sender:String=null, presenceType:String=null, showVal:String=null, statusVal:String=null, priorityVal:Number=0 )
		{
			super( recipient, sender, presenceType, null, "presence" );

			show = showVal;
			status = statusVal;
			priority = priorityVal;
		}

		/**
		 * Serializes the Presence into XML form for sending to a server.
		 *
		 * @return An indication as to whether serialization was successful
		 * @availability Flash Player 7
		 */
		override public function serialize( parentNode:XMLNode ):Boolean
		{
			return super.serialize( parentNode );
		}

		/**
		 * Deserializes an XML object and populates the Presence instance with its data.
		 *
		 * @param xmlNode The XML to deserialize
		 * @return An indication as to whether deserialization was sucessful
		 * @availability Flash Player 7
		 */
		override public function deserialize( xmlNode:XMLNode ):Boolean
		{
			var isDeserialized:Boolean = super.deserialize( xmlNode );

			if (isDeserialized) {
				var children:Array = xmlNode.childNodes;
				for( var i:String in children )
				{
					switch( children[i].nodeName )
					{
						case "show":
							myShowNode = children[i];
							break;

						case "status":
							myStatusNode = children[i];
							break;

						case "priority":
							myPriorityNode = children[i];
							break;
					}
				}
			}
			return isDeserialized;
		}

		/**
		 * The show value; away, online, etc. There are predefined static variables in the Presence
		 * class for this:
		 *

		 *
Presence.SHOW_AWAY

		 *
Presence.SHOW_CHAT

		 *
Presence.SHOW_DND

		 *
Presence.SHOW_NORMAL

		 *
Presence.SHOW_XA

		 *

		 *
		 * @availability Flash Player 7
		 */
		public function get show():String
		{
			if (myShowNode == null) return null;

			if (!exists(myShowNode.firstChild)){
				return Presence.SHOW_NORMAL;
			}
			return myShowNode.firstChild.nodeValue;
		}

		public function set show( showVal:String ):void
		{

			myShowNode = replaceTextNode(getNode(), myShowNode, "show", showVal);
		}

		/**
		 * The status; usually used for "away messages."
		 *
		 * @availability Flash Player 7
		 */
		public function get status():String  {
			if (myStatusNode == null || myStatusNode.firstChild == null) return null;
			return myStatusNode.firstChild.nodeValue;
		}

		public function set status( statusVal:String ):void
		{
			myStatusNode = replaceTextNode(getNode(), myStatusNode, "status", statusVal);
		}

		/**
		 * The priority of the presence, usually on a scale of 1-5.
		 *
		 * @availability Flash Player 7
		 */
		public function get priority():Number
		{
			if (myPriorityNode == null) return NaN;
			var p:Number = Number(myPriorityNode.firstChild.nodeValue);
			if( isNaN( p ) ) {
				return NaN;
			}
			else {
				return p;
			}
		}

		public function set priority( priorityVal:Number ):void
		{
			myPriorityNode = replaceTextNode(getNode(), myPriorityNode, "priority", priorityVal.toString());
		}
	}
}

Make the constants const so it doesn't warn when I use them in data binding expressions

git-svn-id: c197267f952b24206666de142881703007ca05d5@9514 b35dd754-fafc-0310-a699-88a17e54d16e


package org.mangui.HLS.muxing {

	import org.mangui.HLS.muxing.*;
	import org.mangui.HLS.utils.Log;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.utils.ByteArray;
	import flash.utils.Timer;

	/** Representation of an MPEG transport stream. **/
	public class TS extends EventDispatcher {

		/** TS Sync byte. **/
		public static const SYNCBYTE:uint = 0x47;
		/** TS Packet size in byte. **/
		private static const PACKETSIZE:uint = 188;
		/** loop counter to avoid blocking **/
		private static const COUNT:uint = 5000;
		/** Packet ID of the AAC audio stream. **/
		private var _aacId:Number = -1;
		/** List with audio frames. **/
		private var audioTags:Vector.<Tag> = new Vector.<Tag>();
		/** List of packetized elementary streams with AAC. **/
		private var _audioPES:Vector.<PES> = new Vector.<PES>();
		/** has PMT been parsed ? **/
		private var _pmtParsed:Boolean= false;
		/** nb of AV packets before PMT **/
		private var _AVpacketsBeforePMT:Number = 0;
		/** Packet ID of the video stream. **/
		private var _avcId:Number = -1;
		/** PES packet that contains the first keyframe. **/
		private var _firstKey:Number = -1;
		/** Packet ID of the MP3 audio stream. **/
		private var _mp3Id:Number = -1;
		/** Packet ID of the PAT (is always 0). **/
		private var _patId:Number = 0;
		/** Packet ID of the Program Map Table. **/
		private var _pmtId:Number = -1;
		/** List with video frames. **/
		/** Packet ID of the SDT (is always 17). **/
		private var _sdtId:Number = 17;
		private var videoTags:Vector.<Tag> = new Vector.<Tag>();
		/** List of packetized elementary streams with AVC. **/
		private var _videoPES:Vector.<PES> = new Vector.<PES>();
		/** Timer for reading packets **/
		private var _timer:Timer;
		/** Byte data to be read **/
		private var _data:ByteArray;
		/* last PES packet containing AVCC Frame (SPS/PPS) */
		private var _lastAVCCFrame:PES = null;
		/* callback function upon read complete */
		private var _callback:Function;

		/** Transmux the M2TS file into an FLV file. **/
		public function TS(data:ByteArray,callback:Function) {
			// Extract the elementary streams.
			_data = data;
			_callback = callback;
			_timer = new Timer(0,0);
			_timer.addEventListener(TimerEvent.TIMER, _readData);
			_timer.start();
		};

		/** append new TS data */
		//public function appendData(newData:ByteArray):void {
		//  newData.readBytes(_data,_data.length);
		//  _timer.start();
		//}

		/** Read a small chunk of packets each time to avoid blocking **/
		private function _readData(e:Event):void {
			var i:uint = 0;
			while(_data.bytesAvailable && i < COUNT) {
				_readPacket();
				i++;

			}
			if (!_data.bytesAvailable) {
				_timer.stop();
				_extractFrames();
			}
		}

		/** setup the video and audio tag vectors from the read data **/
		private function _extractFrames():void {
			if (_videoPES.length == 0 && _audioPES.length == 0 ) {
				throw new Error("No AAC audio and no AVC video stream found.");
			}
			// Extract the ADTS or MP3 audio frames (transform PES packets into audio tags)
			if(_aacId > 0) {
				_readADTS();
			} else {
				_readMPEG();
			}
			// Extract the NALU video frames (transform PES packets into video tags)
			_readNALU();
			_callback(audioTags,videoTags,_getADIF(),_getAVCC());
		}

		/** Get audio configuration data. **/
		private function _getADIF():ByteArray {
			if(_aacId > 0 && audioTags.length > 0) {
				return AAC.getADIF(_audioPES[0].data,_audioPES[0].payload);
			} else {
				return new ByteArray();
			}
		};

		/** Get video configuration data. **/
		private function _getAVCC():ByteArray {
			if(_firstKey == -1) {
				return new ByteArray();
			}
			return AVC.getAVCC(_lastAVCCFrame.data,_lastAVCCFrame.payload);
		};

		/** Read ADTS frames from audio PES streams. **/
		private function _readADTS():void {
			var frames:Array;
			var overflow:Number = 0;
			var tag:Tag;
			var stamp:Number;
			for(var i:Number=0; i<_audioPES.length; i++) {
				// Parse the PES headers.
				_audioPES[i].parse();
				// Correct for Segmenter's "optimize", which cuts frames in half.
				if(overflow > 0) {
					_audioPES[i-1].data.position = _audioPES[i-1].data.length;
					_audioPES[i-1].data.writeBytes(_audioPES[i].data,_audioPES[i].payload,overflow);
					_audioPES[i].payload += overflow;
				}
				// Store ADTS frames in array.
				frames = AAC.getFrames(_audioPES[i].data,_audioPES[i].payload);
				for(var j:Number=0; j< frames.length; j++) {
					// Increment the timestamp of subsequent frames.
					stamp = Math.round(_audioPES[i].pts + j * 1024 * 1000 / frames[j].rate);
					tag = new Tag(Tag.AAC_RAW, stamp, stamp, false);
					if(i == _audioPES.length-1 && j == frames.length - 1) {
					  if((_audioPES[i].data.length - frames[j].start)>0) {
						  tag.push(_audioPES[i].data, frames[j].start, _audioPES[i].data.length - frames[j].start);
					  }
					} else {
						tag.push(_audioPES[i].data, frames[j].start, frames[j].length);
					}
					audioTags.push(tag);
				}
				// Correct for Segmenter's "optimize", which cuts frames in half.
				overflow = frames[frames.length-1].start +
					frames[frames.length-1].length - _audioPES[i].data.length;
			}
		};

		/** Read MPEG data from audio PES streams. **/
		private function _readMPEG():void {
			var tag:Tag;
			for(var i:Number=0; i<_audioPES.length; i++) {
				_audioPES[i].parse();
				tag = new Tag(Tag.MP3_RAW, _audioPES[i].pts,_audioPES[i].dts, false);
				tag.push(_audioPES[i].data, _audioPES[i].payload, _audioPES[i].data.length-_audioPES[i].payload);
				audioTags.push(tag);
			}
		};

		/** Read NALU frames from video PES streams. **/
		private function _readNALU():void {
			var overflow:Number;
			var units:Array;
			var last:Number;
			for(var i:Number=0; i<_videoPES.length; i++) {
				// Parse the PES headers and NAL units.
				try {
					_videoPES[i].parse();
				} catch (error:Error) {
					Log.txt(error.message);
					continue;
				}
				units = AVC.getNALU(_videoPES[i].data,_videoPES[i].payload);
				// If there's no NAL unit, push all data in the previous tag.
				if(!units.length) {
					videoTags[videoTags.length-1].push(_videoPES[i].data, _videoPES[i].payload,
						_videoPES[i].data.length - _videoPES[i].payload);
					continue;
				}
				// If NAL units are offset, push preceding data into the previous tag.
				overflow = units[0].start - units[0].header - _videoPES[i].payload;
				if(overflow) {
					videoTags[videoTags.length-1].push(_videoPES[i].data,_videoPES[i].payload,overflow);
				}
				videoTags.push(new Tag(Tag.AVC_NALU,_videoPES[i].pts,_videoPES[i].dts,false));
				// Only push NAL units 1 to 5 into tag.
				for(var j:Number = 0; j < units.length; j++) {
					if (units[j].type < 6) {
						videoTags[videoTags.length-1].push(_videoPES[i].data,units[j].start,units[j].length);
						// Unit type 5 indicates a keyframe.
						if(units[j].type == 5) {
							videoTags[videoTags.length-1].keyframe = true;
						}
					} else if (units[j].type == 7 || units[j].type == 8) {
							if(_firstKey == -1) {
								_firstKey = i;
								_lastAVCCFrame=_videoPES[i];
							}
					}
				}
			}
		};

		/** Read TS packet. **/
		private function _readPacket():void {
			// Each packet is 188 bytes.
			var todo:uint = TS.PACKETSIZE;
			// Sync byte.
			if(_data.readByte() != TS.SYNCBYTE) {
				throw new Error("Could not parse TS file: sync byte not found @ offset/len " + _data.position + "/"+ _data.length);
			}
			todo--;
			// Payload unit start indicator.
			var stt:uint = (_data.readUnsignedByte() & 64) >> 6;
			_data.position--;

			// Packet ID (last 13 bits of UI16).
			var pid:uint = _data.readUnsignedShort() & 8191;
			// Check for adaptation field.
			todo -=2;
			var atf:uint = (_data.readByte() & 48) >> 4;
			todo --;
			// Read adaptation field if available.
			if(atf > 1) {
				// Length of adaptation field.
				var len:uint = _data.readUnsignedByte();
				todo--;
				// Random access indicator (keyframe).
				//var rai:uint = data.readUnsignedByte() & 64;
				_data.position += len;
				todo -= len;
				// Return if there's only adaptation field.
				if(atf == 2 || len == 183) {
					_data.position += todo;
					return;
				}
			}

			var pes:ByteArray = new ByteArray();
			// Parse the PES, split by Packet ID.
			switch (pid) {
				case _patId:
					todo -= _readPAT();
					break;
				case _pmtId:
					todo -= _readPMT();
					break;
				case _aacId:
				case _mp3Id:
					if(stt) {
						pes.writeBytes(_data,_data.position,todo);
						_audioPES.push(new PES(pes,true));
					} else if (_audioPES.length) {
						_audioPES[_audioPES.length-1].data.writeBytes(_data,_data.position,todo);
					} else {
						Log.txt("Discarding TS audio packet with id "+pid);
					}
					break;
				case _avcId:
					if(stt) {
						pes.writeBytes(_data,_data.position,todo);
						_videoPES.push(new PES(pes,false));
					} else if (_videoPES.length) {
						_videoPES[_videoPES.length-1].data.writeBytes(_data,_data.position,todo);
					} else {
						Log.txt("Discarding TS video packet with id "+pid + " bad TS segmentation ?");
					}
					break;
				case _sdtId:
						break;
				default:
				_AVpacketsBeforePMT++;
					break;
			}
			// Jump to the next packet.
			_data.position += todo;
		};

		/** Read the Program Association Table. **/
		private function _readPAT():Number {
			// Check the section length for a single PMT.
			_data.position += 3;
			if(_data.readUnsignedByte() > 13) {
				throw new Error("Multiple PMT entries are not supported.");
			}
			// Grab the PMT ID.
			_data.position += 7;
			_pmtId = _data.readUnsignedShort() & 8191;
			return 13;
		};

		/** Read the Program Map Table. **/
		private function _readPMT():Number {
			// Check the section length for a single PMT.
			_data.position += 3;
			var len:uint = _data.readByte();
			var read:uint = 13;
			_data.position += 8;
			var pil:Number = _data.readByte();
			_data.position += pil;
			read += pil;
			// Loop through the streams in the PMT.
			while(read < len) {
				var typ:uint = _data.readByte();
				var sid:uint = _data.readUnsignedShort() & 8191;
				if(typ == 0x0F) {
					_aacId = sid;
				} else if (typ == 0x1B) {
					_avcId = sid;
				} else if (typ == 0x03 || typ == 0x04) {
					_mp3Id = sid;
				}
				//  descriptor loop length
				_data.position++;
				var sel:uint = _data.readByte() & 0x3F;
				_data.position += sel;
				read += sel + 5;
			}
			if(_pmtParsed == false) {
			  _pmtParsed = true;
			// if PMT was not parsed before, and some unknown packets have been skipped in between, rewind to beginning of the stream
			// it helps with fragment not segmented properly (in theory there should be no A/V packets before PAT/PMT)
			  if (_AVpacketsBeforePMT > 1) {
			    Log.txt("late PMT found, rewinding at beginning of TS");
			    return (-_data.position);
			  }
		  }
			return len;
		};
	}
}

package org.mangui.HLS.muxing {

	import org.mangui.HLS.muxing.*;
	import org.mangui.HLS.utils.Log;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.utils.ByteArray;
	import flash.utils.Timer;

	/** Representation of an MPEG transport stream. **/
	public class TS extends EventDispatcher {

		/** TS Sync byte. **/
		private static const SYNCBYTE:uint = 0x47;
		/** TS Packet size in byte. **/
		private static const PACKETSIZE:uint = 188;
		/** loop counter to avoid blocking **/
		private static const COUNT:uint = 5000;
		/** Packet ID of the AAC audio stream. **/
		private var _aacId:Number = -1;
		/** List with audio frames. **/
		private var audioTags:Vector.<Tag> = new Vector.<Tag>();
		/** List of packetized elementary streams with AAC. **/
		private var _audioPES:Vector.<PES> = new Vector.<PES>();
		/** has PMT been parsed ? **/
		private var _pmtParsed:Boolean= false;
		/** any TS packets before PMT ? **/
		private var _packetsBeforePMT:Boolean = false;
		/** Packet ID of the video stream. **/
		private var _avcId:Number = -1;
		/** PES packet that contains the first keyframe. **/
		private var _firstKey:Number = -1;
		/** Packet ID of the MP3 audio stream. **/
		private var _mp3Id:Number = -1;
		/** Packet ID of the PAT (is always 0). **/
		private var _patId:Number = 0;
		/** Packet ID of the Program Map Table. **/
		private var _pmtId:Number = -1;
		/** List with video frames. **/
		/** Packet ID of the SDT (is always 17). **/
		private var _sdtId:Number = 17;
		private var videoTags:Vector.<Tag> = new Vector.<Tag>();
		/** List of packetized elementary streams with AVC. **/
		private var _videoPES:Vector.<PES> = new Vector.<PES>();
		/** Timer for reading packets **/
		private var _timer:Timer;
		/** Byte data to be read **/
		private var _data:ByteArray;
		/* last PES packet containing AVCC Frame (SPS/PPS) */
		private var _lastAVCCFrame:PES = null;
		/* callback function upon read complete */
		private var _callback:Function;

		public static function probe(data:ByteArray):Boolean {
		  var pos:Number = data.position;
		  var len:Number = Math.min(data.bytesAvailable,188);
		  for(var i:Number = 0; i < len ; i++) {
		    if(data.readByte() == SYNCBYTE) {
		      data.position = pos+i;
		      return true;
		    }
		  }
		  data.position = pos;
		  return false;
		}

		/** Transmux the M2TS file into an FLV file. **/
		public function TS(data:ByteArray,callback:Function) {
			// Extract the elementary streams.
			_data = data;
			_callback = callback;
			_timer = new Timer(0,0);
			_timer.addEventListener(TimerEvent.TIMER, _readData);
			_timer.start();
		};

		/** append new TS data */
		//public function appendData(newData:ByteArray):void {
		//  newData.readBytes(_data,_data.length);
		//  _timer.start();
		//}

		/** Read a small chunk of packets each time to avoid blocking **/
		private function _readData(e:Event):void {
			var i:uint = 0;
			while(_data.bytesAvailable && i < COUNT) {
				_readPacket();
				i++;

			}
			if (!_data.bytesAvailable) {
				_timer.stop();
				_extractFrames();
			}
		}

		/** setup the video and audio tag vectors from the read data **/
		private function _extractFrames():void {
			if (_videoPES.length == 0 && _audioPES.length == 0 ) {
				throw new Error("No AAC audio and no AVC video stream found.");
			}
			// Extract the ADTS or MP3 audio frames (transform PES packets into audio tags)
			if(_aacId > 0) {
				_readADTS();
			} else {
				_readMPEG();
			}
			// Extract the NALU video frames (transform PES packets into video tags)
			_readNALU();
			_callback(audioTags,videoTags,_getADIF(),_getAVCC());
		}

		/** Get audio configuration data. **/
		private function _getADIF():ByteArray {
			if(_aacId > 0 && audioTags.length > 0) {
				return AAC.getADIF(_audioPES[0].data,_audioPES[0].payload);
			} else {
				return new ByteArray();
			}
		};

		/** Get video configuration data. **/
		private function _getAVCC():ByteArray {
			if(_firstKey == -1) {
				return new ByteArray();
			}
			return AVC.getAVCC(_lastAVCCFrame.data,_lastAVCCFrame.payload);
		};

		/** Read ADTS frames from audio PES streams. **/
		private function _readADTS():void {
			var frames:Array;
			var overflow:Number = 0;
			var tag:Tag;
			var stamp:Number;
			for(var i:Number=0; i<_audioPES.length; i++) {
				// Parse the PES headers.
				_audioPES[i].parse();
				// Correct for Segmenter's "optimize", which cuts frames in half.
				if(overflow > 0) {
					_audioPES[i-1].data.position = _audioPES[i-1].data.length;
					_audioPES[i-1].data.writeBytes(_audioPES[i].data,_audioPES[i].payload,overflow);
					_audioPES[i].payload += overflow;
				}
				// Store ADTS frames in array.
				frames = AAC.getFrames(_audioPES[i].data,_audioPES[i].payload);
				for(var j:Number=0; j< frames.length; j++) {
					// Increment the timestamp of subsequent frames.
					stamp = Math.round(_audioPES[i].pts + j * 1024 * 1000 / frames[j].rate);
					tag = new Tag(Tag.AAC_RAW, stamp, stamp, false);
					if(i == _audioPES.length-1 && j == frames.length - 1) {
					  if((_audioPES[i].data.length - frames[j].start)>0) {
						  tag.push(_audioPES[i].data, frames[j].start, _audioPES[i].data.length - frames[j].start);
					  }
					} else {
						tag.push(_audioPES[i].data, frames[j].start, frames[j].length);
					}
					audioTags.push(tag);
				}
				// Correct for Segmenter's "optimize", which cuts frames in half.
				overflow = frames[frames.length-1].start +
					frames[frames.length-1].length - _audioPES[i].data.length;
			}
		};

		/** Read MPEG data from audio PES streams. **/
		private function _readMPEG():void {
			var tag:Tag;
			for(var i:Number=0; i<_audioPES.length; i++) {
				_audioPES[i].parse();
				tag = new Tag(Tag.MP3_RAW, _audioPES[i].pts,_audioPES[i].dts, false);
				tag.push(_audioPES[i].data, _audioPES[i].payload, _audioPES[i].data.length-_audioPES[i].payload);
				audioTags.push(tag);
			}
		};

		/** Read NALU frames from video PES streams. **/
		private function _readNALU():void {
			var overflow:Number;
			var units:Array;
			var last:Number;
			for(var i:Number=0; i<_videoPES.length; i++) {
				// Parse the PES headers and NAL units.
				try {
					_videoPES[i].parse();
				} catch (error:Error) {
					Log.txt(error.message);
					continue;
				}
				units = AVC.getNALU(_videoPES[i].data,_videoPES[i].payload);
				// If there's no NAL unit, push all data in the previous tag.
				if(!units.length) {
					videoTags[videoTags.length-1].push(_videoPES[i].data, _videoPES[i].payload,
						_videoPES[i].data.length - _videoPES[i].payload);
					continue;
				}
				// If NAL units are offset, push preceding data into the previous tag.
				overflow = units[0].start - units[0].header - _videoPES[i].payload;
				if(overflow) {
					videoTags[videoTags.length-1].push(_videoPES[i].data,_videoPES[i].payload,overflow);
				}
				videoTags.push(new Tag(Tag.AVC_NALU,_videoPES[i].pts,_videoPES[i].dts,false));
				// Only push NAL units 1 to 5 into tag.
				for(var j:Number = 0; j < units.length; j++) {
					if (units[j].type < 6) {
						videoTags[videoTags.length-1].push(_videoPES[i].data,units[j].start,units[j].length);
						// Unit type 5 indicates a keyframe.
						if(units[j].type == 5) {
							videoTags[videoTags.length-1].keyframe = true;
						}
					} else if (units[j].type == 7 || units[j].type == 8) {
							if(_firstKey == -1) {
								_firstKey = i;
								_lastAVCCFrame=_videoPES[i];
							}
					}
				}
			}
		};

		/** Read TS packet. **/
		private function _readPacket():void {
			// Each packet is 188 bytes.
			var todo:uint = TS.PACKETSIZE;
			// Sync byte.
			if(_data.readByte() != TS.SYNCBYTE) {
			  var pos:Number = _data.position;
			  if(probe(_data) == true) {
			    Log.txt("lost sync in TS, between offsets:" + pos + "/" + _data.position);
			    _data.position++;
			  } else {
				  throw new Error("Could not parse TS file: sync byte not found @ offset/len " + _data.position + "/"+ _data.length);
				}
			}
			todo--;
			// Payload unit start indicator.
			var stt:uint = (_data.readUnsignedByte() & 64) >> 6;
			_data.position--;

			// Packet ID (last 13 bits of UI16).
			var pid:uint = _data.readUnsignedShort() & 8191;
			// Check for adaptation field.
			todo -=2;
			var atf:uint = (_data.readByte() & 48) >> 4;
			todo --;
			// Read adaptation field if available.
			if(atf > 1) {
				// Length of adaptation field.
				var len:uint = _data.readUnsignedByte();
				todo--;
				// Random access indicator (keyframe).
				//var rai:uint = data.readUnsignedByte() & 64;
				_data.position += len;
				todo -= len;
				// Return if there's only adaptation field.
				if(atf == 2 || len == 183) {
					_data.position += todo;
					return;
				}
			}

			var pes:ByteArray = new ByteArray();
			// Parse the PES, split by Packet ID.
			switch (pid) {
				case _patId:
					todo -= _readPAT();
					break;
				case _pmtId:
					todo -= _readPMT();
			    if(_pmtParsed == false) {
			      _pmtParsed = true;
			    // if PMT was not parsed before, and some unknown packets have been skipped in between,
			    // rewind to beginning of the stream, it helps recovering bad segmented content
			    // in theory there should be no A/V packets before PAT/PMT)
			      if (_packetsBeforePMT) {
			        Log.txt("late PMT found, rewinding at beginning of TS");
			        _data.position = 0;
			        return;
			      }
		      }
					break;
				case _aacId:
				case _mp3Id:
					if(stt) {
						pes.writeBytes(_data,_data.position,todo);
						_audioPES.push(new PES(pes,true));
					} else if (_audioPES.length) {
						_audioPES[_audioPES.length-1].data.writeBytes(_data,_data.position,todo);
					} else {
						Log.txt("Discarding TS audio packet with id "+pid);
					}
					break;
				case _avcId:
					if(stt) {
						pes.writeBytes(_data,_data.position,todo);
						_videoPES.push(new PES(pes,false));
					} else if (_videoPES.length) {
						_videoPES[_videoPES.length-1].data.writeBytes(_data,_data.position,todo);
					} else {
						Log.txt("Discarding TS video packet with id "+pid + " bad TS segmentation ?");
					}
					break;
				case _sdtId:
						break;
				default:
				_packetsBeforePMT=true;
					break;
			}
			// Jump to the next packet.
			_data.position += todo;
		};

		/** Read the Program Association Table. **/
		private function _readPAT():Number {
			// Check the section length for a single PMT.
			_data.position += 3;
			if(_data.readUnsignedByte() > 13) {
				throw new Error("Multiple PMT entries are not supported.");
			}
			// Grab the PMT ID.
			_data.position += 7;
			_pmtId = _data.readUnsignedShort() & 8191;
			return 13;
		};

		/** Read the Program Map Table. **/
		private function _readPMT():Number {
			// Check the section length for a single PMT.
			_data.position += 3;
			var len:uint = _data.readByte();
			var read:uint = 13;
			_data.position += 8;
			var pil:Number = _data.readByte();
			_data.position += pil;
			read += pil;
			// Loop through the streams in the PMT.
			while(read < len) {
				var typ:uint = _data.readByte();
				var sid:uint = _data.readUnsignedShort() & 8191;
				if(typ == 0x0F) {
					_aacId = sid;
				} else if (typ == 0x1B) {
					_avcId = sid;
				} else if (typ == 0x03 || typ == 0x04) {
					_mp3Id = sid;
				}
				//  descriptor loop length
				_data.position++;
				var sel:uint = _data.readByte() & 0x3F;
				_data.position += sel;
				read += sel + 5;
			}
			return len;
		};
	}
}

add robustness to TS parsing when sync byte not found, look for it and resynchronize if possible


////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package org.apache.flex.html.beads
{
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.SimpleButton;

	import org.apache.flex.core.BeadViewBase;
	import org.apache.flex.core.IBeadView;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.html.Button;
	import org.apache.flex.html.TitleBar;

    /**
     *  The CloseButtonView class is the view for
     *  the down arrow button in a ScrollBar and other controls.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class CloseButtonView extends BeadViewBase implements IBeadView
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function CloseButtonView()
		{
			upView = new Shape();
			downView = new Shape();
			overView = new Shape();

			drawView(upView.graphics, 0xCCCCCC);
			drawView(downView.graphics, 0x666666);
			drawView(overView.graphics, 0x999999);
		}

		private function drawView(g:Graphics, bgColor:uint):void
		{
			g.beginFill(bgColor);
			g.drawRect(0, 0, 11, 11);
			g.endFill();
		}

		private var shape:Shape;

        /**
         *  @copy org.apache.flex.core.IBead#strand
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		override public function set strand(value:IStrand):void
		{
			super.strand = value;
			shape = new Shape();
			shape.graphics.beginFill(0xCCCCCC);
			shape.graphics.drawRect(0, 0, 11, 11);
			shape.graphics.endFill();
			SimpleButton(value).upState = upView;
			SimpleButton(value).downState = downView;
			SimpleButton(value).overState = overView;
			SimpleButton(value).hitTestState = shape;
		}

		private var upView:Shape;
		private var downView:Shape;
		private var overView:Shape;

	}
}

////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package org.apache.flex.html.beads
{
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.SimpleButton;

	import org.apache.flex.core.BeadViewBase;
	import org.apache.flex.core.IBeadView;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.html.Button;
	import org.apache.flex.html.TitleBar;

    /**
     *  The CloseButtonView class is the view for
     *  the down arrow button in a ScrollBar and other controls.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class CloseButtonView extends BeadViewBase implements IBeadView
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function CloseButtonView()
		{
			upView = new Shape();
			downView = new Shape();
			overView = new Shape();

			drawView(upView.graphics, 0xCCCCCC);
			drawView(downView.graphics, 0x666666);
			drawView(overView.graphics, 0x999999);
		}

		private function drawView(g:Graphics, bgColor:uint):void
		{
			g.beginFill(bgColor);
			g.drawRect(0, 0, 11, 11);
			g.endFill();
            g.lineStyle(2);
            g.moveTo(3,3);
            g.lineTo(8,8);
            g.moveTo(3,8);
            g.lineTo(8,3);
		}

		private var shape:Shape;

        /**
         *  @copy org.apache.flex.core.IBead#strand
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		override public function set strand(value:IStrand):void
		{
			super.strand = value;
			shape = new Shape();
			shape.graphics.beginFill(0xCCCCCC);
			shape.graphics.drawRect(0, 0, 11, 11);
			shape.graphics.endFill();
			SimpleButton(value).upState = upView;
			SimpleButton(value).downState = downView;
			SimpleButton(value).overState = overView;
			SimpleButton(value).hitTestState = shape;
		}

		private var upView:Shape;
		private var downView:Shape;
		private var overView:Shape;

	}
}

add x to close button

add x to close button


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

package com.threerings.crowd.data {

import flash.system.ApplicationDomain;

import com.threerings.util.ClassUtil;
import com.threerings.util.Cloneable;
import com.threerings.util.Integer;
import com.threerings.util.Name;

import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.SimpleStreamableObject;

import com.threerings.presents.dobj.DSet_Entry;

import com.threerings.crowd.data.BodyObject;

/**
 * The occupant info object contains all of the information about an
 * occupant of a place that should be shared with other occupants of the
 * place. These objects are stored in the place object itself and are
 * updated when bodies enter and exit a place.
 *
 * <p> A system that builds upon the Crowd framework can extend this class to
 * include extra information about their occupants. They will need to provide a
 * derived {@link BodyObject} that creates and configures their occupant info
 * in {@link BodyObject#createOccupantInfo}.
 *
 * <p> Note also that this class implements {@link Cloneable} which means
 * that if derived classes add non-primitive attributes, they are
 * responsible for adding the code to clone those attributes when a clone
 * is requested.
 */
public class OccupantInfo extends SimpleStreamableObject
    implements DSet_Entry, Cloneable
{
    /** Constant value for {@link #status}. */
    public static const ACTIVE :int = 0;

    /** Constant value for {@link #status}. */
    public static const IDLE :int = 1;

    /** Constant value for {@link #status}. */
    public static const DISCONNECTED :int = 2;

    /** Maps status codes to human readable strings. */
    public static const X_STATUS :Array = [ "active", "idle", "discon" ];

    /** The body object id of this occupant (and our entry key). */
    public var bodyOid :int;

    /** The username of this occupant. */
    public var username :Name;

    /** The status of this occupant. */
    public var status :int = ACTIVE;

    /**
     * Constructs an occupant info record, optionally obtaining data from the
     * supplied BodyObject.
     */
    public function OccupantInfo (body :BodyObject = null)
    {
        if (body != null) {
            bodyOid = body.getOid();
            username = body.getVisibleName();
            status = body.status;
        }
    }

    /** Access to the body object id as an int. */
    public function getBodyOid () :int
    {
        return bodyOid;
    }

    /**
     * Generates a cloned copy of this instance.
     */
    public function clone () :Object
    {
        var that :OccupantInfo =
            ClassUtil.newInstance(this, ApplicationDomain.currentDomain) as OccupantInfo;
        that.bodyOid = this.bodyOid;
        that.username = this.username;
        that.status = this.status;
        return that;
    }

    // documentation inherited from interface DSet_Entry
    public function getKey () :Object
    {
        return bodyOid;
    }

    // from interface Streamable
    override public function readObject (ins :ObjectInputStream) :void
    {
        super.readObject(ins);
        bodyOid = (ins.readField(Integer) as Integer).value;
        username = (ins.readObject() as Name);
        status = ins.readByte();
    }

    // from interface Streamable
    override public function writeObject (out :ObjectOutputStream) :void
    {
        super.writeObject(out);
        out.writeObject(new Integer(bodyOid));
        out.writeObject(username);
        out.writeByte(status);
    }
}
}

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

package com.threerings.crowd.data {

import flash.system.ApplicationDomain;

import com.threerings.util.ClassUtil;
import com.threerings.util.Cloneable;
import com.threerings.util.Integer;
import com.threerings.util.Name;

import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.SimpleStreamableObject;

import com.threerings.presents.dobj.DSet_Entry;

import com.threerings.crowd.data.BodyObject;

/**
 * The occupant info object contains all of the information about an
 * occupant of a place that should be shared with other occupants of the
 * place. These objects are stored in the place object itself and are
 * updated when bodies enter and exit a place.
 *
 * <p> A system that builds upon the Crowd framework can extend this class to
 * include extra information about their occupants. They will need to provide a
 * derived {@link BodyObject} that creates and configures their occupant info
 * in {@link BodyObject#createOccupantInfo}.
 *
 * <p> Note also that this class implements {@link Cloneable} which means
 * that if derived classes add non-primitive attributes, they are
 * responsible for adding the code to clone those attributes when a clone
 * is requested.
 */
public class OccupantInfo extends SimpleStreamableObject
    implements DSet_Entry, Cloneable
{
    /** Constant value for {@link #status}. */
    public static const ACTIVE :int = 0;

    /** Constant value for {@link #status}. */
    public static const IDLE :int = 1;

    /** Constant value for {@link #status}. */
    public static const DISCONNECTED :int = 2;

    /** Maps status codes to human readable strings. */
    public static const X_STATUS :Array = [ "active", "idle", "discon" ];

    /** The body object id of this occupant (and our entry key). */
    public var bodyOid :int;

    /** The username of this occupant. */
    public var username :Name;

    /** The status of this occupant. */
    public var status :int = ACTIVE;

    /**
     * Constructs an occupant info record, optionally obtaining data from the
     * supplied BodyObject.
     */
    public function OccupantInfo (body :BodyObject = null)
    {
        if (body != null) {
            bodyOid = body.getOid();
            username = body.getVisibleName();
            status = body.status;
        }
    }

    /** Access to the body object id as an int. */
    public function getBodyOid () :int
    {
        return bodyOid;
    }

    /**
     * Generates a cloned copy of this instance.
     */
    public function clone () :Object
    {
        var that :OccupantInfo =
            ClassUtil.newInstance(this) as OccupantInfo;
        that.bodyOid = this.bodyOid;
        that.username = this.username;
        that.status = this.status;
        return that;
    }

    // documentation inherited from interface DSet_Entry
    public function getKey () :Object
    {
        return bodyOid;
    }

    // from interface Streamable
    override public function readObject (ins :ObjectInputStream) :void
    {
        super.readObject(ins);
        bodyOid = (ins.readField(Integer) as Integer).value;
        username = (ins.readObject() as Name);
        status = ins.readByte();
    }

    // from interface Streamable
    override public function writeObject (out :ObjectOutputStream) :void
    {
        super.writeObject(out);
        out.writeObject(new Integer(bodyOid));
        out.writeObject(username);
        out.writeByte(status);
    }
}
}

Revert this, I'll work it another way.

Revert this, I'll work it another way.

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