package com.kaltura.kdpfl.view.controls
{
	import com.hybrid.ui.ToolTip;
	import com.kaltura.kdpfl.style.TextFormatManager;

	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.geom.Point;
	import flash.text.TextField;
	import flash.utils.Timer;

	public class ToolTipManager extends Sprite
	{
		private static var _instance:ToolTipManager;
		private var _labelText:TextField;
		private var _foregroundLayer:DisplayObjectContainer;
		private var _timerLapse:int=1000
		private var _timer:Timer=new Timer(_timerLapse,1)
		private var _dataTip:ToolTip;
		private var _zeroPoint : Point = new Point(0,0);
		private var _edgeTolerance:Number	= .1;
		private var _showHook:Boolean		= false;
		private var _hookSize:Number		= 5;
		private var _autoSize:Boolean		= true;
		private var _cornerRadius:Number	= 0;
		private var _hookAlignment:String;
		private var _textBuffer:Number		= 10;
		private var _follow:Boolean			= false;
		protected var _offsetX : Number = 0;
		protected var _offsetY : Number = 30;
		protected var _tooltipWidth : Number = 100;
		protected var _tooltipHeight : Number;
		//Tooltip delay in milliseconds
		protected var _delay : Number;
		protected var _paddingX : Number = 0;

		public function ToolTipManager(enforcer:Enforcer){}

		public static function getInstance():ToolTipManager
		{
			if(_instance == null)
				_instance = new ToolTipManager(new Enforcer());

			return _instance;
		}

		public function set foregroundLayer(value:DisplayObjectContainer):void
		{
			_foregroundLayer = value;
		}

		public function showToolTip(message:String,target:DisplayObject):void
		{
			//Set tooltip parameters
			_dataTip= new ToolTip();
			_dataTip.autoSize		= _autoSize;
			_dataTip.follow			= false;
			_dataTip.hook			= _showHook;
			_dataTip.cornerRadius	= _cornerRadius;
			_dataTip.hookSize		= _hookSize;
			_dataTip.follow			= _follow;
			if (_tooltipHeight)
				_dataTip.tipHeight = _tooltipHeight;
			if (_delay)
				_dataTip.delay = _delay;

			_dataTip.buffer = 		_textBuffer;

			//getting the possition of the stage
			var positionX : int = target.stage.mouseX;
			var targetPoint:Point=new Point(target.x,target.y);

			var rightLimitX : Number = target.root.parent.localToGlobal(new Point(target.root.parent.x, target.root.parent.y) ).x + target.root.parent.width;

			var leftLimitX : Number = target.root.parent.x;

			//find left rail
			var totalTolerancePixels:Number	= target.stage.width * _edgeTolerance;
			if(positionX < totalTolerancePixels)
				_dataTip.align			= "right";
			else if(positionX > (target.stage.width - totalTolerancePixels))
				_dataTip.align			= "left";
			else
				_dataTip.align			= "center";

			//override all settings it set
			if(!_hookAlignment)
				_dataTip.align			= _hookAlignment;

			if (target.parent.localToGlobal(targetPoint).x + _tooltipWidth + offsetX  > rightLimitX)
			{
				positionX = rightLimitX - tooltipWidth + offsetX + paddingX;
			}
			else if (target.parent.localToGlobal(targetPoint).x + offsetX  < leftLimitX)
			{
				//In case of negative offsetX
				positionX = - target.parent.localToGlobal(targetPoint).x - offsetX + paddingX;
			}
			else
			{
				positionX = target.parent.localToGlobal(targetPoint).x + offsetX + paddingX;
			}
			var positionY : int = target.stage.mouseY

			if(target.localToGlobal(targetPoint).y - _offsetY >0)
			{
				positionY=(target.localToGlobal(targetPoint).y-_offsetY);
			}
			else
			{
				positionY=(target.localToGlobal(targetPoint).y+_offsetY);
			}

			_dataTip.titleFormat = TextFormatManager.getInstance().getTextFormat( "toolTip_label" );
			//usage of new tooltip class
			//http://blog.hy-brid.com/flash/25/simple-as3-tooltip/
			if(message!=""){
				//_dataTip.show(_foregroundLayer,message,positionX,positionY,target.stage.width,target.stage.height,null)
				_dataTip.show(_foregroundLayer,message,null, positionX, positionY);
			}

		}

		public function updateTitle(value:String):void{
			if(_dataTip)
				_dataTip.updateTitle(value);
		}

		//tooltips that fall within this percentile to side edges will receive a different hook point.
		//hook values "left, right, center". value in decimals
		public function set edgeTolerance(value : Number):void{
			_edgeTolerance	 = value;
		}

		public function get edgeTolerance():Number{
			return _edgeTolerance;
		}

		public function set textBuffer(value : Number):void{
			_textBuffer	 = value;
		}

		public function get textBuffer():Number{
			return _textBuffer;
		}

		//left, right, center.
		public function set hookAlignment(value : String):void{
			_hookAlignment	 = value;
		}

		public function get hookAlignment():String{
			return _hookAlignment;
		}

		public function set hookSize(value : Number):void{
			_hookSize	 = value;
		}

		public function get hookSize():Number{
			return _hookSize;
		}

		public function set showHook(value:Boolean):void{
			_showHook 		= value;
		}

		public function get showHook():Boolean{
			return _showHook;
		}

		public function set cornerRadius(value:Number):void{
			_cornerRadius 		= value;
		}

		public function get cornerRadius():Number{
			return _cornerRadius;
		}

		public function set autoSize(value:Boolean):void{
			_autoSize 		= value;
		}

		public function get autoSize():Boolean{
			return _autoSize;
		}

		public function destroyToolTip():void
		{
			if (_dataTip)
				_dataTip.hide()
		}

		public function get offsetX():Number
		{
			return _offsetX;
		}

		public function set offsetX(value:Number):void
		{
			_offsetX = value;
		}

		public function get follow():Boolean
		{
			return _follow;
		}

		public function set follow(value:Boolean):void
		{
			_follow = value;
		}

		public function get offsetY():Number
		{
			return _offsetY;
		}

		public function set offsetY(value:Number):void
		{
			_offsetY = value;
		}

		public function get tooltipWidth():Number
		{
			return _tooltipWidth;
		}

		public function set tooltipWidth(value:Number):void
		{
			_tooltipWidth = value;
		}

		public function get tooltipHeight():Number
		{
			return _tooltipHeight;
		}

		public function set tooltipHeight(value:Number):void
		{
			_tooltipHeight = value;
		}

		public function get delay():Number
		{
			return _delay;
		}

		public function set delay(value:Number):void
		{
			_delay = value;
		}

		public function get paddingX():Number
		{
			return _paddingX;
		}

		public function set paddingX(value:Number):void
		{
			_paddingX = value;
		}

	}
}

class Enforcer{}

package com.kaltura.kdpfl.view.controls
{
	import com.hybrid.ui.ToolTip;
	import com.kaltura.kdpfl.style.TextFormatManager;

	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.geom.Point;
	import flash.text.TextField;
	import flash.utils.Timer;

	public class ToolTipManager extends Sprite
	{
		private static var _instance:ToolTipManager;
		private var _labelText:TextField;
		private var _foregroundLayer:DisplayObjectContainer;
		private var _timerLapse:int=1000
		private var _timer:Timer=new Timer(_timerLapse,1)
		private var _dataTip:ToolTip;
		private var _zeroPoint : Point = new Point(0,0);
		private var _edgeTolerance:Number	= .1;
		private var _showHook:Boolean		= false;
		private var _hookSize:Number		= 5;
		private var _autoSize:Boolean		= true;
		private var _cornerRadius:Number	= 0;
		private var _hookAlignment:String;
		private var _textBuffer:Number		= 10;
		private var _follow:Boolean			= false;
		protected var _offsetX : Number = 0;
		protected var _offsetY : Number = 30;
		protected var _tooltipWidth : Number = 100;
		protected var _tooltipHeight : Number;
		//Tooltip delay in milliseconds
		protected var _delay : Number;
		protected var _paddingX : Number = 0;

		public function ToolTipManager(enforcer:Enforcer){}

		public static function getInstance():ToolTipManager
		{
			if(_instance == null)
				_instance = new ToolTipManager(new Enforcer());

			return _instance;
		}

		public function set foregroundLayer(value:DisplayObjectContainer):void
		{
			_foregroundLayer = value;
		}

		public function showToolTip(message:String,target:DisplayObject):void
		{
			//Set tooltip parameters
			_dataTip= new ToolTip();
			_dataTip.autoSize		= _autoSize;
			_dataTip.follow			= false;
			_dataTip.hook			= _showHook;
			_dataTip.cornerRadius	= _cornerRadius;
			_dataTip.hookSize		= _hookSize;
			_dataTip.follow			= _follow;
			if (_tooltipHeight)
				_dataTip.tipHeight = _tooltipHeight;
			if (_delay)
				_dataTip.delay = _delay;

			_dataTip.buffer = 		_textBuffer;

			//getting the possition of the stage
			var positionX : int = target.stage.mouseX;
			var targetPoint:Point=new Point(target.x,target.y);

			var rightLimitX : Number = target.root.parent.localToGlobal(new Point(target.root.parent.x, target.root.parent.y) ).x + target.root.parent.width;

			var leftLimitX : Number = target.root.parent.x;

			//find left rail
			var totalTolerancePixels:Number	= target.stage.width * _edgeTolerance;
			if(positionX < totalTolerancePixels)
				_dataTip.align			= "right";
			else if(positionX > (target.stage.width - totalTolerancePixels))
				_dataTip.align			= "left";
			else
				_dataTip.align			= "center";

			//override all settings it set
			if(_hookAlignment)
				_dataTip.align			= _hookAlignment;

			if (target.parent.localToGlobal(targetPoint).x + _tooltipWidth + offsetX  > rightLimitX)
			{
				positionX = rightLimitX - tooltipWidth + offsetX + paddingX;
			}
			else if (target.parent.localToGlobal(targetPoint).x + offsetX  < leftLimitX)
			{
				//In case of negative offsetX
				positionX = - target.parent.localToGlobal(targetPoint).x - offsetX + paddingX;
			}
			else
			{
				positionX = target.parent.localToGlobal(targetPoint).x + offsetX + paddingX;
			}
			var positionY : int = target.stage.mouseY

			if(target.localToGlobal(targetPoint).y - _offsetY >0)
			{
				positionY=(target.localToGlobal(targetPoint).y-_offsetY);
			}
			else
			{
				positionY=(target.localToGlobal(targetPoint).y+_offsetY);
			}

			_dataTip.titleFormat = TextFormatManager.getInstance().getTextFormat( "toolTip_label" );
			//usage of new tooltip class
			//http://blog.hy-brid.com/flash/25/simple-as3-tooltip/
			if(message!=""){
				//_dataTip.show(_foregroundLayer,message,positionX,positionY,target.stage.width,target.stage.height,null)
				_dataTip.show(_foregroundLayer,message,null, positionX, positionY);
			}

		}

		public function updateTitle(value:String):void{
			if(_dataTip)
				_dataTip.updateTitle(value);
		}

		//tooltips that fall within this percentile to side edges will receive a different hook point.
		//hook values "left, right, center". value in decimals
		public function set edgeTolerance(value : Number):void{
			_edgeTolerance	 = value;
		}

		public function get edgeTolerance():Number{
			return _edgeTolerance;
		}

		public function set textBuffer(value : Number):void{
			_textBuffer	 = value;
		}

		public function get textBuffer():Number{
			return _textBuffer;
		}

		//left, right, center.
		public function set hookAlignment(value : String):void{
			_hookAlignment	 = value;
		}

		public function get hookAlignment():String{
			return _hookAlignment;
		}

		public function set hookSize(value : Number):void{
			_hookSize	 = value;
		}

		public function get hookSize():Number{
			return _hookSize;
		}

		public function set showHook(value:Boolean):void{
			_showHook 		= value;
		}

		public function get showHook():Boolean{
			return _showHook;
		}

		public function set cornerRadius(value:Number):void{
			_cornerRadius 		= value;
		}

		public function get cornerRadius():Number{
			return _cornerRadius;
		}

		public function set autoSize(value:Boolean):void{
			_autoSize 		= value;
		}

		public function get autoSize():Boolean{
			return _autoSize;
		}

		public function destroyToolTip():void
		{
			if (_dataTip)
				_dataTip.hide()
		}

		public function get offsetX():Number
		{
			return _offsetX;
		}

		public function set offsetX(value:Number):void
		{
			_offsetX = value;
		}

		public function get follow():Boolean
		{
			return _follow;
		}

		public function set follow(value:Boolean):void
		{
			_follow = value;
		}

		public function get offsetY():Number
		{
			return _offsetY;
		}

		public function set offsetY(value:Number):void
		{
			_offsetY = value;
		}

		public function get tooltipWidth():Number
		{
			return _tooltipWidth;
		}

		public function set tooltipWidth(value:Number):void
		{
			_tooltipWidth = value;
		}

		public function get tooltipHeight():Number
		{
			return _tooltipHeight;
		}

		public function set tooltipHeight(value:Number):void
		{
			_tooltipHeight = value;
		}

		public function get delay():Number
		{
			return _delay;
		}

		public function set delay(value:Number):void
		{
			_delay = value;
		}

		public function get paddingX():Number
		{
			return _paddingX;
		}

		public function set paddingX(value:Number):void
		{
			_paddingX = value;
		}

	}
}

class Enforcer{}

Update kdp3Lib/src/com/kaltura/kdpfl/view/controls/ToolTipManager.as

Fixed wrong logic when checking if there is a _hookAlignment


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls.stream {
    import flash.events.TimerEvent;
    import flash.events.Event;
    import flash.utils.Timer;
    import flash.utils.Dictionary;

    import org.mangui.hls.event.HLSMediatime;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.constant.HLSSeekStates;
    import org.mangui.hls.constant.HLSSeekMode;
    import org.mangui.hls.utils.Log;
    import org.mangui.hls.flv.FLVTag;
    import org.mangui.hls.HLS;
    import org.mangui.hls.HLSSettings;

    /*
     * intermediate FLV Tag Buffer
     *  input : FLV tags retrieved from different fragment loaders (video/alt-audio...)
     *  output : provide muxed FLV tags to HLSNetStream
     */
    public class TagBuffer {
        private var _hls : HLS;
        /** Timer used to process FLV tags. **/
        private var _timer : Timer;
        private var _audioTags : Vector.<FLVData>;
        private var _videoTags : Vector.<FLVData>;
        private var _metaTags : Vector.<FLVData>;
        /** playlist duration **/
        private var _playlist_duration : Number = 0;
        /** requested start position **/
        private var _seek_position_requested : Number;
        /** real start position , retrieved from first fragment **/
        private var _seek_position_real : Number;
        /** start position of first injected tag **/
        private var _first_start_position : Number;
        private var _seek_pos_reached : Boolean;
        /** playlist sliding (non null for live playlist) **/
        private var _playlist_sliding_duration : Number;
        /** buffer PTS (indexed by continuity counter)  */
        private var _buffer_pts : Dictionary;

        public function TagBuffer(hls : HLS) {
            _hls = hls;
            flushAll();
            _timer = new Timer(100, 0);
            _timer.addEventListener(TimerEvent.TIMER, _checkBuffer);
            _hls.addEventListener(HLSEvent.PLAYLIST_DURATION_UPDATED, _playlistDurationUpdated);
        }

        public function dispose() : void {
            flushAll();
            _hls.removeEventListener(HLSEvent.PLAYLIST_DURATION_UPDATED, _playlistDurationUpdated);
            _timer.stop();
            _hls = null;
            _timer = null;
        }

        public function stop() : void {
            flushAll();
        }

        public function seek(position : Number) : void {
            _seek_position_requested = position;
            flushAll();
            _timer.start();
        }

        public function flushAll() : void {
            _audioTags = new Vector.<FLVData>();
            _videoTags = new Vector.<FLVData>();
            _metaTags = new Vector.<FLVData>();
            _buffer_pts = new Dictionary();
            _seek_pos_reached = false;
            _playlist_sliding_duration = 0;
            _first_start_position = -1;
        }

        public function flushAudio() : void {
            _audioTags = new Vector.<FLVData>();
        }

        public function appendTags(tags : Vector.<FLVTag>, min_pts : Number, max_pts : Number, continuity : int, start_position : Number) : void {
            for each (var tag : FLVTag in tags) {
                var position : Number = start_position + (tag.pts - min_pts) / 1000;
                var tagData : FLVData = new FLVData(tag, position, continuity);
                switch(tag.type) {
                    case FLVTag.AAC_HEADER:
                    case FLVTag.AAC_RAW:
                    case FLVTag.MP3_RAW:
                        _audioTags.push(tagData);
                        break;
                    case FLVTag.AVC_HEADER:
                    case FLVTag.AVC_NALU:
                        _videoTags.push(tagData);
                        break;
                    case FLVTag.DISCONTINUITY:
                    case FLVTag.METADATA:
                        _metaTags.push(tagData);
                        break;
                    default:
                }
            }
            /* check live playlist sliding here :
            _seek_position_real + getTotalBufferedDuration()  should be the start_position
             * /of the new fragment if the playlist was not sliding
            => live playlist sliding is the difference between the new start position  and this previous value */
            if (_hls.seekState == HLSSeekStates.SEEKED) {
                // Log.info("seek pos/getTotalBufferedDuration/start pos:" + _seek_position_requested + "/" + getTotalBufferedDuration() + "/" + start_position);
                _playlist_sliding_duration = (_first_start_position + getTotalBufferedDuration()) - start_position;
            } else {
                if (_first_start_position == -1) {
                    // remember position of first tag injected after seek. it will be used for playlist sliding computation
                    _first_start_position = start_position;
                }
            }
            // update buffer min/max table indexed with continuity counter
            if (_buffer_pts[continuity] == undefined) {
                _buffer_pts[continuity] = new BufferPTS(min_pts, max_pts);
            } else {
                (_buffer_pts[continuity] as BufferPTS).max = max_pts;
            }
            _timer.start();
        }

        /** Return current media position **/
        public function get position() : Number {
            switch(_hls.seekState) {
                case HLSSeekStates.SEEKING:
                    return  _seek_position_requested;
                case HLSSeekStates.SEEKED:
                    /** Relative playback position = (Absolute Position(seek position + play time) - playlist sliding, non null for Live Playlist) **/
                    return _seek_position_real + _hls.stream.time - _playlist_sliding_duration;
                case HLSSeekStates.IDLE:
                default:
                    return 0;
            }
        };

        public function get audioBufferLength() : Number {
            return getbuflen(_audioTags);
        }

        public function get videoBufferLength() : Number {
            return getbuflen(_videoTags);
        }

        public function get bufferLength() : Number {
            return Math.max(audioBufferLength, videoBufferLength);
        }

        /**  Timer **/
        private function _checkBuffer(e : Event) : void {
            // dispatch media time event
            _hls.dispatchEvent(new HLSEvent(HLSEvent.MEDIA_TIME, new HLSMediatime(position, _playlist_duration, _hls.stream.bufferLength, _playlist_sliding_duration)));

            /* only append tags if seek position has been reached, otherwise wait for more tags to come
             * this is to ensure that accurate seeking will work appropriately
             */
            if (_seek_pos_reached || max_pos >= _seek_position_requested) {
                var flvdata : FLVData;
                var data : Vector.<FLVData> = new Vector.<FLVData>();
                while ((flvdata = shift()) != null) {
                    data.push(flvdata);
                }
                if (!_seek_pos_reached) {
                    data = seekFilterTags(data);
                    _seek_pos_reached = true;
                }

                var tags : Vector.<FLVTag> = new Vector.<FLVTag>();
                for each (flvdata in data) {
                    tags.push(flvdata.tag);
                }
                if (tags.length) {
                    (_hls.stream as HLSNetStream).appendTags(tags);
                    Log.debug("appending " + tags.length + " tags");
                }
            }
        }

        /* filter/tweak tags to seek accurately into the stream */
        private function seekFilterTags(tags : Vector.<FLVData>) : Vector.<FLVData> {
            var filteredTags : Vector.<FLVData>=  new Vector.<FLVData>();
            /* PTS of first tag that will be pushed into FLV tag buffer */
            var first_pts : Number;
            /* PTS of last video keyframe before requested seek position */
            var keyframe_pts : Number;
            /* */
            var min_offset : Number = tags[0].position;
            var min_pts : Number = tags[0].tag.pts;
            /*
             *
             *    real seek       requested seek                 Frag
             *     position           position                    End
             *        *------------------*-------------------------
             *        <------------------>
             *             seek_offset
             *
             * real seek position is the start offset of the first received fragment after seek command. (= fragment start offset).
             * seek offset is the diff between the requested seek position and the real seek position
             */

            /* if requested seek position is out of this segment bounds
             * all the segments will be pushed, first pts should be thus be min_pts
             */
            if (_seek_position_requested < min_offset) {
                _seek_position_real = min_offset;
                first_pts = min_pts;
            } else {
                /* if requested position is within segment bounds, determine real seek position depending on seek mode setting */
                if (HLSSettings.seekMode == HLSSeekMode.SEGMENT_SEEK) {
                    _seek_position_real = min_offset;
                    first_pts = min_pts;
                } else {
                    /* accurate or keyframe seeking */
                    /* seek_pts is the requested PTS seek position */
                    var seek_pts : Number = min_pts + 1000 * (_seek_position_requested - min_offset);
                    /* analyze fragment tags and look for PTS of last keyframe before seek position.*/
                    keyframe_pts = min_pts;
                    for each (var flvData : FLVData in tags) {
                        var tag : FLVTag = flvData.tag;
                        // look for last keyframe with pts <= seek_pts
                        if (tag.keyframe == true && tag.pts <= seek_pts && (tag.type == FLVTag.AVC_HEADER || tag.type == FLVTag.AVC_NALU)) {
                            keyframe_pts = tag.pts;
                        }
                    }
                    if (HLSSettings.seekMode == HLSSeekMode.KEYFRAME_SEEK) {
                        _seek_position_real = min_offset + (keyframe_pts - min_pts) / 1000;
                        first_pts = keyframe_pts;
                    } else {
                        // accurate seek, to exact requested position
                        _seek_position_real = _seek_position_requested;
                        first_pts = seek_pts;
                    }
                }
            }
            /* if in segment seeking mode : push all FLV tags */
            if (HLSSettings.seekMode == HLSSeekMode.SEGMENT_SEEK) {
                filteredTags = tags;
            } else {
                /* keyframe / accurate seeking, we need to filter out some FLV tags */
                for each (flvData in tags) {
                    tag = flvData.tag;
                    if (tag.pts >= first_pts) {
                        filteredTags.push(flvData);
                    } else {
                        switch(tag.type) {
                            case FLVTag.AAC_HEADER:
                            case FLVTag.AVC_HEADER:
                            case FLVTag.METADATA:
                            case FLVTag.DISCONTINUITY:
                                tag.pts = tag.dts = first_pts;
                                filteredTags.push(flvData);
                                break;
                            case FLVTag.AVC_NALU:
                                /* only append video tags starting from last keyframe before seek position to avoid playback artifacts
                                 *  rationale of this is that there can be multiple keyframes per segment. if we append all keyframes
                                 *  in NetStream, all of them will be displayed in a row and this will introduce some playback artifacts
                                 *  */
                                if (tag.pts >= keyframe_pts) {
                                    tag.pts = tag.dts = first_pts;
                                    filteredTags.push(flvData);
                                }
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
            return filteredTags;
        }

        private function _playlistDurationUpdated(event : HLSEvent) : void {
            _playlist_duration = event.duration;
        }

        private function getbuflen(tags : Vector.<FLVData>) : Number {
            var min_pts : Number = 0;
            var max_pts : Number = 0;
            var continuity : int = -1;
            var len : Number = 0;

            for each (var data : FLVData in tags) {
                if (data.continuity != continuity) {
                    len += (max_pts - min_pts);
                    min_pts = data.tag.pts;
                    continuity = data.continuity;
                } else {
                    max_pts = data.tag.pts;
                }
            }
            len += (max_pts - min_pts);
            return len / 1000;
        }

        /** return total buffered duration since seek() call, needed to compute live playlist sliding  */
        private function getTotalBufferedDuration() : Number {
            var len : Number = 0;
            for each (var entry : BufferPTS in _buffer_pts) {
                len += (entry.max - entry.min);
            }
            return len / 1000;
        }

        /*
         *
         * return next tag from queue, using the following priority :
         * smallest continuity
         * then smallest pts
         * then metadata then video then audio tags
         */
        private function shift() : FLVData {
            if (_videoTags.length == 0 && _audioTags.length == 0 && _metaTags.length == 0)
                return null;

            var continuity : int = int.MAX_VALUE;
            // find smallest continuity counter
            if (_metaTags.length) continuity = Math.min(continuity, _metaTags[0].continuity);
            if (_videoTags.length) continuity = Math.min(continuity, _videoTags[0].continuity);
            if (_audioTags.length) continuity = Math.min(continuity, _audioTags[0].continuity);

            var pts : Number = Number.MAX_VALUE;
            // for this continuity counter, find smallest PTS
            if (_metaTags.length && _metaTags[0].continuity == continuity) pts = Math.min(pts, _metaTags[0].tag.pts);
            if (_videoTags.length && _videoTags[0].continuity == continuity) pts = Math.min(pts, _videoTags[0].tag.pts);
            if (_audioTags.length && _audioTags[0].continuity == continuity) pts = Math.min(pts, _audioTags[0].tag.pts);

            // for this continuity counter, this PTS, prioritize tags with the following order : metadata/video/audio
            if (_metaTags.length && _metaTags[0].continuity == continuity && _metaTags[0].tag.pts == pts) return _metaTags.shift();
            if (_videoTags.length && _videoTags[0].continuity == continuity && _videoTags[0].tag.pts == pts) return _videoTags.shift();
            else return _audioTags.shift();
        }

        /*
        private function get min_pos() : Number {
        var min_pos_ : Number = Number.POSITIVE_INFINITY;
        if (_metaTags.length) min_pos_ = Math.min(min_pos_, _metaTags[0].position);
        if (_videoTags.length) min_pos_ = Math.min(min_pos_, _videoTags[0].position);
        if (_audioTags.length) min_pos_ = Math.min(min_pos_, _audioTags[0].position);
        return min_pos_;
        }
         */
        private function get max_pos() : Number {
            var max_pos_ : Number = Number.NEGATIVE_INFINITY;
            if (_metaTags.length) max_pos_ = Math.max(max_pos_, _metaTags[_metaTags.length - 1].position);
            if (_videoTags.length) max_pos_ = Math.max(max_pos_, _videoTags[_videoTags.length - 1].position);
            if (_audioTags.length) max_pos_ = Math.max(max_pos_, _audioTags[_audioTags.length - 1].position);
            return max_pos_;
        }
    }
}

import org.mangui.hls.flv.FLVTag;

class FLVData {
    public var tag : FLVTag;
    public var position : Number;
    public var continuity : int;

    public function FLVData(tag : FLVTag, position : Number, continuity : int) {
        this.tag = tag;
        this.position = position;
        this.continuity = continuity;
    }
}

class BufferPTS {
    public var min : Number;
    public var max : Number;

    public function BufferPTS(min : Number, max : Number) {
        this.min = min;
        this.max = max;
    }
}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls.stream {
    import flash.events.TimerEvent;
    import flash.events.Event;
    import flash.utils.Timer;
    import flash.utils.Dictionary;

    import org.mangui.hls.event.HLSMediatime;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.constant.HLSSeekStates;
    import org.mangui.hls.constant.HLSSeekMode;
    import org.mangui.hls.flv.FLVTag;
    import org.mangui.hls.HLS;
    import org.mangui.hls.HLSSettings;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    /*
     * intermediate FLV Tag Buffer
     *  input : FLV tags retrieved from different fragment loaders (video/alt-audio...)
     *  output : provide muxed FLV tags to HLSNetStream
     */
    public class TagBuffer {
        private var _hls : HLS;
        /** Timer used to process FLV tags. **/
        private var _timer : Timer;
        private var _audioTags : Vector.<FLVData>;
        private var _videoTags : Vector.<FLVData>;
        private var _metaTags : Vector.<FLVData>;
        /** playlist duration **/
        private var _playlist_duration : Number = 0;
        /** requested start position **/
        private var _seek_position_requested : Number;
        /** real start position , retrieved from first fragment **/
        private var _seek_position_real : Number;
        /** start position of first injected tag **/
        private var _first_start_position : Number;
        private var _seek_pos_reached : Boolean;
        /** playlist sliding (non null for live playlist) **/
        private var _playlist_sliding_duration : Number;
        /** buffer PTS (indexed by continuity counter)  */
        private var _buffer_pts : Dictionary;

        public function TagBuffer(hls : HLS) {
            _hls = hls;
            flushAll();
            _timer = new Timer(100, 0);
            _timer.addEventListener(TimerEvent.TIMER, _checkBuffer);
            _hls.addEventListener(HLSEvent.PLAYLIST_DURATION_UPDATED, _playlistDurationUpdated);
        }

        public function dispose() : void {
            flushAll();
            _hls.removeEventListener(HLSEvent.PLAYLIST_DURATION_UPDATED, _playlistDurationUpdated);
            _timer.stop();
            _hls = null;
            _timer = null;
        }

        public function stop() : void {
            flushAll();
        }

        public function seek(position : Number) : void {
            _seek_position_requested = position;
            flushAll();
            _timer.start();
        }

        public function flushAll() : void {
            _audioTags = new Vector.<FLVData>();
            _videoTags = new Vector.<FLVData>();
            _metaTags = new Vector.<FLVData>();
            _buffer_pts = new Dictionary();
            _seek_pos_reached = false;
            _playlist_sliding_duration = 0;
            _first_start_position = -1;
        }

        public function flushAudio() : void {
            _audioTags = new Vector.<FLVData>();
        }

        public function appendTags(tags : Vector.<FLVTag>, min_pts : Number, max_pts : Number, continuity : int, start_position : Number) : void {
            for each (var tag : FLVTag in tags) {
                var position : Number = start_position + (tag.pts - min_pts) / 1000;
                var tagData : FLVData = new FLVData(tag, position, continuity);
                switch(tag.type) {
                    case FLVTag.AAC_HEADER:
                    case FLVTag.AAC_RAW:
                    case FLVTag.MP3_RAW:
                        _audioTags.push(tagData);
                        break;
                    case FLVTag.AVC_HEADER:
                    case FLVTag.AVC_NALU:
                        _videoTags.push(tagData);
                        break;
                    case FLVTag.DISCONTINUITY:
                    case FLVTag.METADATA:
                        _metaTags.push(tagData);
                        break;
                    default:
                }
            }
            /* check live playlist sliding here :
            _seek_position_real + getTotalBufferedDuration()  should be the start_position
             * /of the new fragment if the playlist was not sliding
            => live playlist sliding is the difference between the new start position  and this previous value */
            if (_hls.seekState == HLSSeekStates.SEEKED) {
                // Log.info("seek pos/getTotalBufferedDuration/start pos:" + _seek_position_requested + "/" + getTotalBufferedDuration() + "/" + start_position);
                _playlist_sliding_duration = (_first_start_position + getTotalBufferedDuration()) - start_position;
            } else {
                if (_first_start_position == -1) {
                    // remember position of first tag injected after seek. it will be used for playlist sliding computation
                    _first_start_position = start_position;
                }
            }
            // update buffer min/max table indexed with continuity counter
            if (_buffer_pts[continuity] == undefined) {
                _buffer_pts[continuity] = new BufferPTS(min_pts, max_pts);
            } else {
                (_buffer_pts[continuity] as BufferPTS).max = max_pts;
            }
            _timer.start();
        }

        /** Return current media position **/
        public function get position() : Number {
            switch(_hls.seekState) {
                case HLSSeekStates.SEEKING:
                    return  _seek_position_requested;
                case HLSSeekStates.SEEKED:
                    /** Relative playback position = (Absolute Position(seek position + play time) - playlist sliding, non null for Live Playlist) **/
                    return _seek_position_real + _hls.stream.time - _playlist_sliding_duration;
                case HLSSeekStates.IDLE:
                default:
                    return 0;
            }
        };

        public function get audioBufferLength() : Number {
            return getbuflen(_audioTags);
        }

        public function get videoBufferLength() : Number {
            return getbuflen(_videoTags);
        }

        public function get bufferLength() : Number {
            return Math.max(audioBufferLength, videoBufferLength);
        }

        /**  Timer **/
        private function _checkBuffer(e : Event) : void {
            // dispatch media time event
            _hls.dispatchEvent(new HLSEvent(HLSEvent.MEDIA_TIME, new HLSMediatime(position, _playlist_duration, _hls.stream.bufferLength, _playlist_sliding_duration)));

            /* only append tags if seek position has been reached, otherwise wait for more tags to come
             * this is to ensure that accurate seeking will work appropriately
             */
            if (_seek_pos_reached || max_pos >= _seek_position_requested) {
                var flvdata : FLVData;
                var data : Vector.<FLVData> = new Vector.<FLVData>();
                while ((flvdata = shift()) != null) {
                    data.push(flvdata);
                }
                if (!_seek_pos_reached) {
                    data = seekFilterTags(data);
                    _seek_pos_reached = true;
                }

                var tags : Vector.<FLVTag> = new Vector.<FLVTag>();
                for each (flvdata in data) {
                    tags.push(flvdata.tag);
                }
                if (tags.length) {
                    (_hls.stream as HLSNetStream).appendTags(tags);
                    CONFIG::LOGGING {
                        Log.debug2("appending " + tags.length + " tags");
                    }
                }
            }
        }

        /* filter/tweak tags to seek accurately into the stream */
        private function seekFilterTags(tags : Vector.<FLVData>) : Vector.<FLVData> {
            var filteredTags : Vector.<FLVData>=  new Vector.<FLVData>();
            /* PTS of first tag that will be pushed into FLV tag buffer */
            var first_pts : Number;
            /* PTS of last video keyframe before requested seek position */
            var keyframe_pts : Number;
            /* */
            var min_offset : Number = tags[0].position;
            var min_pts : Number = tags[0].tag.pts;
            /*
             *
             *    real seek       requested seek                 Frag
             *     position           position                    End
             *        *------------------*-------------------------
             *        <------------------>
             *             seek_offset
             *
             * real seek position is the start offset of the first received fragment after seek command. (= fragment start offset).
             * seek offset is the diff between the requested seek position and the real seek position
             */

            /* if requested seek position is out of this segment bounds
             * all the segments will be pushed, first pts should be thus be min_pts
             */
            if (_seek_position_requested < min_offset) {
                _seek_position_real = min_offset;
                first_pts = min_pts;
            } else {
                /* if requested position is within segment bounds, determine real seek position depending on seek mode setting */
                if (HLSSettings.seekMode == HLSSeekMode.SEGMENT_SEEK) {
                    _seek_position_real = min_offset;
                    first_pts = min_pts;
                } else {
                    /* accurate or keyframe seeking */
                    /* seek_pts is the requested PTS seek position */
                    var seek_pts : Number = min_pts + 1000 * (_seek_position_requested - min_offset);
                    /* analyze fragment tags and look for PTS of last keyframe before seek position.*/
                    keyframe_pts = min_pts;
                    for each (var flvData : FLVData in tags) {
                        var tag : FLVTag = flvData.tag;
                        // look for last keyframe with pts <= seek_pts
                        if (tag.keyframe == true && tag.pts <= seek_pts && (tag.type == FLVTag.AVC_HEADER || tag.type == FLVTag.AVC_NALU)) {
                            keyframe_pts = tag.pts;
                        }
                    }
                    if (HLSSettings.seekMode == HLSSeekMode.KEYFRAME_SEEK) {
                        _seek_position_real = min_offset + (keyframe_pts - min_pts) / 1000;
                        first_pts = keyframe_pts;
                    } else {
                        // accurate seek, to exact requested position
                        _seek_position_real = _seek_position_requested;
                        first_pts = seek_pts;
                    }
                }
            }
            /* if in segment seeking mode : push all FLV tags */
            if (HLSSettings.seekMode == HLSSeekMode.SEGMENT_SEEK) {
                filteredTags = tags;
            } else {
                /* keyframe / accurate seeking, we need to filter out some FLV tags */
                for each (flvData in tags) {
                    tag = flvData.tag;
                    if (tag.pts >= first_pts) {
                        filteredTags.push(flvData);
                    } else {
                        switch(tag.type) {
                            case FLVTag.AAC_HEADER:
                            case FLVTag.AVC_HEADER:
                            case FLVTag.METADATA:
                            case FLVTag.DISCONTINUITY:
                                tag.pts = tag.dts = first_pts;
                                filteredTags.push(flvData);
                                break;
                            case FLVTag.AVC_NALU:
                                /* only append video tags starting from last keyframe before seek position to avoid playback artifacts
                                 *  rationale of this is that there can be multiple keyframes per segment. if we append all keyframes
                                 *  in NetStream, all of them will be displayed in a row and this will introduce some playback artifacts
                                 *  */
                                if (tag.pts >= keyframe_pts) {
                                    tag.pts = tag.dts = first_pts;
                                    filteredTags.push(flvData);
                                }
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
            return filteredTags;
        }

        private function _playlistDurationUpdated(event : HLSEvent) : void {
            _playlist_duration = event.duration;
        }

        private function getbuflen(tags : Vector.<FLVData>) : Number {
            var min_pts : Number = 0;
            var max_pts : Number = 0;
            var continuity : int = -1;
            var len : Number = 0;

            for each (var data : FLVData in tags) {
                if (data.continuity != continuity) {
                    len += (max_pts - min_pts);
                    min_pts = data.tag.pts;
                    continuity = data.continuity;
                } else {
                    max_pts = data.tag.pts;
                }
            }
            len += (max_pts - min_pts);
            return len / 1000;
        }

        /** return total buffered duration since seek() call, needed to compute live playlist sliding  */
        private function getTotalBufferedDuration() : Number {
            var len : Number = 0;
            for each (var entry : BufferPTS in _buffer_pts) {
                len += (entry.max - entry.min);
            }
            return len / 1000;
        }

        /*
         *
         * return next tag from queue, using the following priority :
         * smallest continuity
         * then smallest pts
         * then metadata then video then audio tags
         */
        private function shift() : FLVData {
            if (_videoTags.length == 0 && _audioTags.length == 0 && _metaTags.length == 0)
                return null;

            var continuity : int = int.MAX_VALUE;
            // find smallest continuity counter
            if (_metaTags.length) continuity = Math.min(continuity, _metaTags[0].continuity);
            if (_videoTags.length) continuity = Math.min(continuity, _videoTags[0].continuity);
            if (_audioTags.length) continuity = Math.min(continuity, _audioTags[0].continuity);

            var pts : Number = Number.MAX_VALUE;
            // for this continuity counter, find smallest PTS
            if (_metaTags.length && _metaTags[0].continuity == continuity) pts = Math.min(pts, _metaTags[0].tag.pts);
            if (_videoTags.length && _videoTags[0].continuity == continuity) pts = Math.min(pts, _videoTags[0].tag.pts);
            if (_audioTags.length && _audioTags[0].continuity == continuity) pts = Math.min(pts, _audioTags[0].tag.pts);

            // for this continuity counter, this PTS, prioritize tags with the following order : metadata/video/audio
            if (_metaTags.length && _metaTags[0].continuity == continuity && _metaTags[0].tag.pts == pts) return _metaTags.shift();
            if (_videoTags.length && _videoTags[0].continuity == continuity && _videoTags[0].tag.pts == pts) return _videoTags.shift();
            else return _audioTags.shift();
        }

        /*
        private function get min_pos() : Number {
        var min_pos_ : Number = Number.POSITIVE_INFINITY;
        if (_metaTags.length) min_pos_ = Math.min(min_pos_, _metaTags[0].position);
        if (_videoTags.length) min_pos_ = Math.min(min_pos_, _videoTags[0].position);
        if (_audioTags.length) min_pos_ = Math.min(min_pos_, _audioTags[0].position);
        return min_pos_;
        }
         */
        private function get max_pos() : Number {
            var max_pos_ : Number = Number.NEGATIVE_INFINITY;
            if (_metaTags.length) max_pos_ = Math.max(max_pos_, _metaTags[_metaTags.length - 1].position);
            if (_videoTags.length) max_pos_ = Math.max(max_pos_, _videoTags[_videoTags.length - 1].position);
            if (_audioTags.length) max_pos_ = Math.max(max_pos_, _audioTags[_audioTags.length - 1].position);
            return max_pos_;
        }
    }
}

import org.mangui.hls.flv.FLVTag;

class FLVData {
    public var tag : FLVTag;
    public var position : Number;
    public var continuity : int;

    public function FLVData(tag : FLVTag, position : Number, continuity : int) {
        this.tag = tag;
        this.position = position;
        this.continuity = continuity;
    }
}

class BufferPTS {
    public var min : Number;
    public var max : Number;

    public function BufferPTS(min : Number, max : Number) {
        this.min = min;
        this.max = max;
    }
}

reduce log verbosity

reduce log verbosity


// Urho3D editor settings dialog

bool subscribedToEditorSettings = false;
Window@ settingsDialog;

void CreateEditorSettingsDialog()
{
    if (settingsDialog !is null)
        return;

    settingsDialog = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorSettingsDialog.xml"));
    ui.root.AddChild(settingsDialog);
    settingsDialog.opacity = uiMaxOpacity;
    settingsDialog.height = 440;
    CenterDialog(settingsDialog);
    UpdateEditorSettingsDialog();
    HideEditorSettingsDialog();
}

void UpdateEditorSettingsDialog()
{
    if (settingsDialog is null)
        return;

    LineEdit@ nearClipEdit = settingsDialog.GetChild("NearClipEdit", true);
    nearClipEdit.text = String(viewNearClip);

    LineEdit@ farClipEdit = settingsDialog.GetChild("FarClipEdit", true);
    farClipEdit.text = String(viewFarClip);

    LineEdit@ fovEdit = settingsDialog.GetChild("FOVEdit", true);
    fovEdit.text = String(viewFov);

    LineEdit@ speedEdit = settingsDialog.GetChild("SpeedEdit", true);
    speedEdit.text = String(cameraBaseSpeed);

    CheckBox@ limitRotationToggle = settingsDialog.GetChild("LimitRotationToggle", true);
    limitRotationToggle.checked = limitRotation;

    CheckBox@ mouseWheelCameraPositionToggle = settingsDialog.GetChild("MouseWheelCameraPositionToggle", true);
    mouseWheelCameraPositionToggle.checked = mouseWheelCameraPosition;

    DropDownList@ MouseOrbitEdit = settingsDialog.GetChild("MouseOrbitEdit", true);
    MouseOrbitEdit.selection = mouseOrbitMode;

    LineEdit@ distanceEdit = settingsDialog.GetChild("DistanceEdit", true);
    distanceEdit.text = String(newNodeDistance);

    LineEdit@ moveStepEdit = settingsDialog.GetChild("MoveStepEdit", true);
    moveStepEdit.text = String(moveStep);
    CheckBox@ moveSnapToggle = settingsDialog.GetChild("MoveSnapToggle", true);
    moveSnapToggle.checked = moveSnap;

    LineEdit@ rotateStepEdit = settingsDialog.GetChild("RotateStepEdit", true);
    rotateStepEdit.text = String(rotateStep);
    CheckBox@ rotateSnapToggle = settingsDialog.GetChild("RotateSnapToggle", true);
    rotateSnapToggle.checked = rotateSnap;

    LineEdit@ scaleStepEdit = settingsDialog.GetChild("ScaleStepEdit", true);
    scaleStepEdit.text = String(scaleStep);
    CheckBox@ scaleSnapToggle = settingsDialog.GetChild("ScaleSnapToggle", true);
    scaleSnapToggle.checked = scaleSnap;

    CheckBox@ applyMaterialListToggle = settingsDialog.GetChild("ApplyMaterialListToggle", true);
    applyMaterialListToggle.checked = applyMaterialList;

    CheckBox@ rememberResourcePathToggle = settingsDialog.GetChild("RememberResourcePathToggle", true);
    rememberResourcePathToggle.checked = rememberResourcePath;

    LineEdit@ importOptionsEdit = settingsDialog.GetChild("ImportOptionsEdit", true);
    importOptionsEdit.text = importOptions;

    DropDownList@ pickModeEdit = settingsDialog.GetChild("PickModeEdit", true);
    pickModeEdit.selection = pickMode;

    DropDownList@ textureQualityEdit = settingsDialog.GetChild("TextureQualityEdit", true);
    textureQualityEdit.selection = renderer.textureQuality;

    DropDownList@ materialQualityEdit = settingsDialog.GetChild("MaterialQualityEdit", true);
    materialQualityEdit.selection = renderer.materialQuality;

    DropDownList@ shadowResolutionEdit = settingsDialog.GetChild("ShadowResolutionEdit", true);
    shadowResolutionEdit.selection = GetShadowResolution();

    DropDownList@ shadowQualityEdit = settingsDialog.GetChild("ShadowQualityEdit", true);
    shadowQualityEdit.selection = renderer.shadowQuality;

    LineEdit@ maxOccluderTrianglesEdit = settingsDialog.GetChild("MaxOccluderTrianglesEdit", true);
    maxOccluderTrianglesEdit.text = String(renderer.maxOccluderTriangles);

    CheckBox@ specularLightingToggle = settingsDialog.GetChild("SpecularLightingToggle", true);
    specularLightingToggle.checked = renderer.specularLighting;

    CheckBox@ dynamicInstancingToggle = settingsDialog.GetChild("DynamicInstancingToggle", true);
    dynamicInstancingToggle.checked = renderer.dynamicInstancing;

    CheckBox@ frameLimiterToggle = settingsDialog.GetChild("FrameLimiterToggle", true);
    frameLimiterToggle.checked = engine.maxFps > 0;

    if (!subscribedToEditorSettings)
    {
        SubscribeToEvent(nearClipEdit, "TextChanged", "EditCameraNearClip");
        SubscribeToEvent(nearClipEdit, "TextFinished", "EditCameraNearClip");
        SubscribeToEvent(farClipEdit, "TextChanged", "EditCameraFarClip");
        SubscribeToEvent(farClipEdit, "TextFinished", "EditCameraFarClip");
        SubscribeToEvent(fovEdit, "TextChanged", "EditCameraFOV");
        SubscribeToEvent(fovEdit, "TextFinished", "EditCameraFOV");
        SubscribeToEvent(speedEdit, "TextChanged", "EditCameraSpeed");
        SubscribeToEvent(speedEdit, "TextFinished", "EditCameraSpeed");
        SubscribeToEvent(limitRotationToggle, "Toggled", "EditLimitRotation");
        SubscribeToEvent(mouseWheelCameraPositionToggle, "Toggled", "EditMouseWheelCameraPosition");
        SubscribeToEvent(MouseOrbitEdit, "ItemSelected", "EditMouseOrbitMode");
        SubscribeToEvent(distanceEdit, "TextChanged", "EditNewNodeDistance");
        SubscribeToEvent(distanceEdit, "TextFinished", "EditNewNodeDistance");
        SubscribeToEvent(moveStepEdit, "TextChanged", "EditMoveStep");
        SubscribeToEvent(moveStepEdit, "TextFinished", "EditMoveStep");
        SubscribeToEvent(rotateStepEdit, "TextChanged", "EditRotateStep");
        SubscribeToEvent(rotateStepEdit, "TextFinished", "EditRotateStep");
        SubscribeToEvent(scaleStepEdit, "TextChanged", "EditScaleStep");
        SubscribeToEvent(scaleStepEdit, "TextFinished", "EditScaleStep");
        SubscribeToEvent(moveSnapToggle, "Toggled", "EditMoveSnap");
        SubscribeToEvent(rotateSnapToggle, "Toggled", "EditRotateSnap");
        SubscribeToEvent(scaleSnapToggle, "Toggled", "EditScaleSnap");
        SubscribeToEvent(rememberResourcePathToggle, "Toggled", "EditRememberResourcePath");
        SubscribeToEvent(applyMaterialListToggle, "Toggled", "EditApplyMaterialList");
        SubscribeToEvent(importOptionsEdit, "TextChanged", "EditImportOptions");
        SubscribeToEvent(importOptionsEdit, "TextFinished", "EditImportOptions");
        SubscribeToEvent(pickModeEdit, "ItemSelected", "EditPickMode");
        SubscribeToEvent(textureQualityEdit, "ItemSelected", "EditTextureQuality");
        SubscribeToEvent(materialQualityEdit, "ItemSelected", "EditMaterialQuality");
        SubscribeToEvent(shadowResolutionEdit, "ItemSelected", "EditShadowResolution");
        SubscribeToEvent(shadowQualityEdit, "ItemSelected", "EditShadowQuality");
        SubscribeToEvent(maxOccluderTrianglesEdit, "TextChanged", "EditMaxOccluderTriangles");
        SubscribeToEvent(maxOccluderTrianglesEdit, "TextFinished", "EditMaxOccluderTriangles");
        SubscribeToEvent(specularLightingToggle, "Toggled", "EditSpecularLighting");
        SubscribeToEvent(dynamicInstancingToggle, "Toggled", "EditDynamicInstancing");
        SubscribeToEvent(frameLimiterToggle, "Toggled", "EditFrameLimiter");
        SubscribeToEvent(settingsDialog.GetChild("CloseButton", true), "Released", "HideEditorSettingsDialog");
        subscribedToEditorSettings = true;
    }
}

bool ShowEditorSettingsDialog()
{
    UpdateEditorSettingsDialog();
    settingsDialog.visible = true;
    settingsDialog.BringToFront();
    return true;
}

void HideEditorSettingsDialog()
{
    settingsDialog.visible = false;
}

void EditCameraNearClip(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    viewNearClip = edit.text.ToFloat();
    UpdateViewParameters();
    if (eventType == StringHash("TextFinished"))
        edit.text = String(camera.nearClip);
}

void EditCameraFarClip(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    viewFarClip = edit.text.ToFloat();
    UpdateViewParameters();
    if (eventType == StringHash("TextFinished"))
        edit.text = String(camera.farClip);
}

void EditCameraFOV(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    viewFov = edit.text.ToFloat();
    UpdateViewParameters();
    if (eventType == StringHash("TextFinished"))
        edit.text = String(camera.fov);
}

void EditCameraSpeed(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    cameraBaseSpeed = Max(edit.text.ToFloat(), 1.0);
    if (eventType == StringHash("TextFinished"))
        edit.text = String(cameraBaseSpeed);
}

void EditLimitRotation(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    limitRotation = edit.checked;
}

void EditMouseWheelCameraPosition(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    mouseWheelCameraPosition = edit.checked;
}

void EditMouseOrbitMode(StringHash eventType, VariantMap& eventData)
{
    DropDownList@ edit = eventData["Element"].GetPtr();
    mouseOrbitMode = edit.selection;
}

void EditNewNodeDistance(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    newNodeDistance = Max(edit.text.ToFloat(), 0.0);
    if (eventType == StringHash("TextFinished"))
        edit.text = String(newNodeDistance);
}

void EditMoveStep(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    moveStep = Max(edit.text.ToFloat(), 0.0);
    if (eventType == StringHash("TextFinished"))
        edit.text = String(moveStep);
}

void EditRotateStep(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    rotateStep = Max(edit.text.ToFloat(), 0.0);
    if (eventType == StringHash("TextFinished"))
        edit.text = String(rotateStep);
}

void EditScaleStep(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    scaleStep = Max(edit.text.ToFloat(), 0.0);
    if (eventType == StringHash("TextFinished"))
        edit.text = String(scaleStep);
}

void EditMoveSnap(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    moveSnap = edit.checked;
    toolBarDirty = true;
}

void EditRotateSnap(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    rotateSnap = edit.checked;
    toolBarDirty = true;
}

void EditScaleSnap(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    scaleSnap = edit.checked;
    toolBarDirty = true;
}

void EditRememberResourcePath(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    rememberResourcePath = edit.checked;
}

void EditApplyMaterialList(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    applyMaterialList = edit.checked;
}

void EditImportOptions(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    importOptions = edit.text.Trimmed();
}

void EditPickMode(StringHash eventType, VariantMap& eventData)
{
    DropDownList@ edit = eventData["Element"].GetPtr();
    pickMode = edit.selection;
}

void EditTextureQuality(StringHash eventType, VariantMap& eventData)
{
    DropDownList@ edit = eventData["Element"].GetPtr();
    renderer.textureQuality = edit.selection;
}

void EditMaterialQuality(StringHash eventType, VariantMap& eventData)
{
    DropDownList@ edit = eventData["Element"].GetPtr();
    renderer.materialQuality = edit.selection;
}

void EditShadowResolution(StringHash eventType, VariantMap& eventData)
{
    DropDownList@ edit = eventData["Element"].GetPtr();
    SetShadowResolution(edit.selection);
}

void EditShadowQuality(StringHash eventType, VariantMap& eventData)
{
    DropDownList@ edit = eventData["Element"].GetPtr();
    renderer.shadowQuality = edit.selection;
}

void EditMaxOccluderTriangles(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    renderer.maxOccluderTriangles = edit.text.ToInt();
    if (eventType == StringHash("TextFinished"))
        edit.text = String(renderer.maxOccluderTriangles);
}

void EditSpecularLighting(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    renderer.specularLighting = edit.checked;
}

void EditDynamicInstancing(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    renderer.dynamicInstancing = edit.checked;
}

void EditFrameLimiter(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    engine.maxFps = edit.checked ? 200 : 0;
}

// Urho3D editor settings dialog

bool subscribedToEditorSettings = false;
Window@ settingsDialog;

void CreateEditorSettingsDialog()
{
    if (settingsDialog !is null)
        return;

    settingsDialog = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorSettingsDialog.xml"));
    ui.root.AddChild(settingsDialog);
    settingsDialog.opacity = uiMaxOpacity;
    settingsDialog.height = 440;
    CenterDialog(settingsDialog);
    UpdateEditorSettingsDialog();
    HideEditorSettingsDialog();
}

void UpdateEditorSettingsDialog()
{
    if (settingsDialog is null)
        return;

    LineEdit@ nearClipEdit = settingsDialog.GetChild("NearClipEdit", true);
    nearClipEdit.text = String(viewNearClip);

    LineEdit@ farClipEdit = settingsDialog.GetChild("FarClipEdit", true);
    farClipEdit.text = String(viewFarClip);

    LineEdit@ fovEdit = settingsDialog.GetChild("FOVEdit", true);
    fovEdit.text = String(viewFov);

    LineEdit@ speedEdit = settingsDialog.GetChild("SpeedEdit", true);
    speedEdit.text = String(cameraBaseSpeed);

    CheckBox@ limitRotationToggle = settingsDialog.GetChild("LimitRotationToggle", true);
    limitRotationToggle.checked = limitRotation;

    CheckBox@ mouseWheelCameraPositionToggle = settingsDialog.GetChild("MouseWheelCameraPositionToggle", true);
    mouseWheelCameraPositionToggle.checked = mouseWheelCameraPosition;

    DropDownList@ mouseOrbitEdit = settingsDialog.GetChild("MouseOrbitEdit", true);
    mouseOrbitEdit.selection = mouseOrbitMode;

    LineEdit@ distanceEdit = settingsDialog.GetChild("DistanceEdit", true);
    distanceEdit.text = String(newNodeDistance);

    LineEdit@ moveStepEdit = settingsDialog.GetChild("MoveStepEdit", true);
    moveStepEdit.text = String(moveStep);
    CheckBox@ moveSnapToggle = settingsDialog.GetChild("MoveSnapToggle", true);
    moveSnapToggle.checked = moveSnap;

    LineEdit@ rotateStepEdit = settingsDialog.GetChild("RotateStepEdit", true);
    rotateStepEdit.text = String(rotateStep);
    CheckBox@ rotateSnapToggle = settingsDialog.GetChild("RotateSnapToggle", true);
    rotateSnapToggle.checked = rotateSnap;

    LineEdit@ scaleStepEdit = settingsDialog.GetChild("ScaleStepEdit", true);
    scaleStepEdit.text = String(scaleStep);
    CheckBox@ scaleSnapToggle = settingsDialog.GetChild("ScaleSnapToggle", true);
    scaleSnapToggle.checked = scaleSnap;

    CheckBox@ applyMaterialListToggle = settingsDialog.GetChild("ApplyMaterialListToggle", true);
    applyMaterialListToggle.checked = applyMaterialList;

    CheckBox@ rememberResourcePathToggle = settingsDialog.GetChild("RememberResourcePathToggle", true);
    rememberResourcePathToggle.checked = rememberResourcePath;

    LineEdit@ importOptionsEdit = settingsDialog.GetChild("ImportOptionsEdit", true);
    importOptionsEdit.text = importOptions;

    DropDownList@ pickModeEdit = settingsDialog.GetChild("PickModeEdit", true);
    pickModeEdit.selection = pickMode;

    DropDownList@ textureQualityEdit = settingsDialog.GetChild("TextureQualityEdit", true);
    textureQualityEdit.selection = renderer.textureQuality;

    DropDownList@ materialQualityEdit = settingsDialog.GetChild("MaterialQualityEdit", true);
    materialQualityEdit.selection = renderer.materialQuality;

    DropDownList@ shadowResolutionEdit = settingsDialog.GetChild("ShadowResolutionEdit", true);
    shadowResolutionEdit.selection = GetShadowResolution();

    DropDownList@ shadowQualityEdit = settingsDialog.GetChild("ShadowQualityEdit", true);
    shadowQualityEdit.selection = renderer.shadowQuality;

    LineEdit@ maxOccluderTrianglesEdit = settingsDialog.GetChild("MaxOccluderTrianglesEdit", true);
    maxOccluderTrianglesEdit.text = String(renderer.maxOccluderTriangles);

    CheckBox@ specularLightingToggle = settingsDialog.GetChild("SpecularLightingToggle", true);
    specularLightingToggle.checked = renderer.specularLighting;

    CheckBox@ dynamicInstancingToggle = settingsDialog.GetChild("DynamicInstancingToggle", true);
    dynamicInstancingToggle.checked = renderer.dynamicInstancing;

    CheckBox@ frameLimiterToggle = settingsDialog.GetChild("FrameLimiterToggle", true);
    frameLimiterToggle.checked = engine.maxFps > 0;

    if (!subscribedToEditorSettings)
    {
        SubscribeToEvent(nearClipEdit, "TextChanged", "EditCameraNearClip");
        SubscribeToEvent(nearClipEdit, "TextFinished", "EditCameraNearClip");
        SubscribeToEvent(farClipEdit, "TextChanged", "EditCameraFarClip");
        SubscribeToEvent(farClipEdit, "TextFinished", "EditCameraFarClip");
        SubscribeToEvent(fovEdit, "TextChanged", "EditCameraFOV");
        SubscribeToEvent(fovEdit, "TextFinished", "EditCameraFOV");
        SubscribeToEvent(speedEdit, "TextChanged", "EditCameraSpeed");
        SubscribeToEvent(speedEdit, "TextFinished", "EditCameraSpeed");
        SubscribeToEvent(limitRotationToggle, "Toggled", "EditLimitRotation");
        SubscribeToEvent(mouseWheelCameraPositionToggle, "Toggled", "EditMouseWheelCameraPosition");
        SubscribeToEvent(mouseOrbitEdit, "ItemSelected", "EditMouseOrbitMode");
        SubscribeToEvent(distanceEdit, "TextChanged", "EditNewNodeDistance");
        SubscribeToEvent(distanceEdit, "TextFinished", "EditNewNodeDistance");
        SubscribeToEvent(moveStepEdit, "TextChanged", "EditMoveStep");
        SubscribeToEvent(moveStepEdit, "TextFinished", "EditMoveStep");
        SubscribeToEvent(rotateStepEdit, "TextChanged", "EditRotateStep");
        SubscribeToEvent(rotateStepEdit, "TextFinished", "EditRotateStep");
        SubscribeToEvent(scaleStepEdit, "TextChanged", "EditScaleStep");
        SubscribeToEvent(scaleStepEdit, "TextFinished", "EditScaleStep");
        SubscribeToEvent(moveSnapToggle, "Toggled", "EditMoveSnap");
        SubscribeToEvent(rotateSnapToggle, "Toggled", "EditRotateSnap");
        SubscribeToEvent(scaleSnapToggle, "Toggled", "EditScaleSnap");
        SubscribeToEvent(rememberResourcePathToggle, "Toggled", "EditRememberResourcePath");
        SubscribeToEvent(applyMaterialListToggle, "Toggled", "EditApplyMaterialList");
        SubscribeToEvent(importOptionsEdit, "TextChanged", "EditImportOptions");
        SubscribeToEvent(importOptionsEdit, "TextFinished", "EditImportOptions");
        SubscribeToEvent(pickModeEdit, "ItemSelected", "EditPickMode");
        SubscribeToEvent(textureQualityEdit, "ItemSelected", "EditTextureQuality");
        SubscribeToEvent(materialQualityEdit, "ItemSelected", "EditMaterialQuality");
        SubscribeToEvent(shadowResolutionEdit, "ItemSelected", "EditShadowResolution");
        SubscribeToEvent(shadowQualityEdit, "ItemSelected", "EditShadowQuality");
        SubscribeToEvent(maxOccluderTrianglesEdit, "TextChanged", "EditMaxOccluderTriangles");
        SubscribeToEvent(maxOccluderTrianglesEdit, "TextFinished", "EditMaxOccluderTriangles");
        SubscribeToEvent(specularLightingToggle, "Toggled", "EditSpecularLighting");
        SubscribeToEvent(dynamicInstancingToggle, "Toggled", "EditDynamicInstancing");
        SubscribeToEvent(frameLimiterToggle, "Toggled", "EditFrameLimiter");
        SubscribeToEvent(settingsDialog.GetChild("CloseButton", true), "Released", "HideEditorSettingsDialog");
        subscribedToEditorSettings = true;
    }
}

bool ShowEditorSettingsDialog()
{
    UpdateEditorSettingsDialog();
    settingsDialog.visible = true;
    settingsDialog.BringToFront();
    return true;
}

void HideEditorSettingsDialog()
{
    settingsDialog.visible = false;
}

void EditCameraNearClip(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    viewNearClip = edit.text.ToFloat();
    UpdateViewParameters();
    if (eventType == StringHash("TextFinished"))
        edit.text = String(camera.nearClip);
}

void EditCameraFarClip(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    viewFarClip = edit.text.ToFloat();
    UpdateViewParameters();
    if (eventType == StringHash("TextFinished"))
        edit.text = String(camera.farClip);
}

void EditCameraFOV(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    viewFov = edit.text.ToFloat();
    UpdateViewParameters();
    if (eventType == StringHash("TextFinished"))
        edit.text = String(camera.fov);
}

void EditCameraSpeed(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    cameraBaseSpeed = Max(edit.text.ToFloat(), 1.0);
    if (eventType == StringHash("TextFinished"))
        edit.text = String(cameraBaseSpeed);
}

void EditLimitRotation(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    limitRotation = edit.checked;
}

void EditMouseWheelCameraPosition(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    mouseWheelCameraPosition = edit.checked;
}

void EditMouseOrbitMode(StringHash eventType, VariantMap& eventData)
{
    DropDownList@ edit = eventData["Element"].GetPtr();
    mouseOrbitMode = edit.selection;
}

void EditNewNodeDistance(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    newNodeDistance = Max(edit.text.ToFloat(), 0.0);
    if (eventType == StringHash("TextFinished"))
        edit.text = String(newNodeDistance);
}

void EditMoveStep(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    moveStep = Max(edit.text.ToFloat(), 0.0);
    if (eventType == StringHash("TextFinished"))
        edit.text = String(moveStep);
}

void EditRotateStep(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    rotateStep = Max(edit.text.ToFloat(), 0.0);
    if (eventType == StringHash("TextFinished"))
        edit.text = String(rotateStep);
}

void EditScaleStep(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    scaleStep = Max(edit.text.ToFloat(), 0.0);
    if (eventType == StringHash("TextFinished"))
        edit.text = String(scaleStep);
}

void EditMoveSnap(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    moveSnap = edit.checked;
    toolBarDirty = true;
}

void EditRotateSnap(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    rotateSnap = edit.checked;
    toolBarDirty = true;
}

void EditScaleSnap(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    scaleSnap = edit.checked;
    toolBarDirty = true;
}

void EditRememberResourcePath(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    rememberResourcePath = edit.checked;
}

void EditApplyMaterialList(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    applyMaterialList = edit.checked;
}

void EditImportOptions(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    importOptions = edit.text.Trimmed();
}

void EditPickMode(StringHash eventType, VariantMap& eventData)
{
    DropDownList@ edit = eventData["Element"].GetPtr();
    pickMode = edit.selection;
}

void EditTextureQuality(StringHash eventType, VariantMap& eventData)
{
    DropDownList@ edit = eventData["Element"].GetPtr();
    renderer.textureQuality = edit.selection;
}

void EditMaterialQuality(StringHash eventType, VariantMap& eventData)
{
    DropDownList@ edit = eventData["Element"].GetPtr();
    renderer.materialQuality = edit.selection;
}

void EditShadowResolution(StringHash eventType, VariantMap& eventData)
{
    DropDownList@ edit = eventData["Element"].GetPtr();
    SetShadowResolution(edit.selection);
}

void EditShadowQuality(StringHash eventType, VariantMap& eventData)
{
    DropDownList@ edit = eventData["Element"].GetPtr();
    renderer.shadowQuality = edit.selection;
}

void EditMaxOccluderTriangles(StringHash eventType, VariantMap& eventData)
{
    LineEdit@ edit = eventData["Element"].GetPtr();
    renderer.maxOccluderTriangles = edit.text.ToInt();
    if (eventType == StringHash("TextFinished"))
        edit.text = String(renderer.maxOccluderTriangles);
}

void EditSpecularLighting(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    renderer.specularLighting = edit.checked;
}

void EditDynamicInstancing(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    renderer.dynamicInstancing = edit.checked;
}

void EditFrameLimiter(StringHash eventType, VariantMap& eventData)
{
    CheckBox@ edit = eventData["Element"].GetPtr();
    engine.maxFps = edit.checked ? 200 : 0;
}

Change variable name to start with lowercase.


////////////////////////////////////////////////////////////////////////////////
//
//  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 mx.core
{
import flash.system.Capabilities;

import mx.core.mx_internal;

use namespace mx_internal;

/**
 *  The RuntimeDPIProvider class provides the default mapping of
 *  similar device DPI values into predefined DPI classes.
 *  An Application may have its runtimeDPIProvider property set to a
 *  subclass of RuntimeDPIProvider to override Flex's default mappings.
 *  Overriding Flex's default mappings will cause changes in the Application's
 *  automatic scaling behavior.
 *
 *
Overriding Flex's default mappings is usually only necessary for devices
 *  that incorrectly report their screenDPI and for devices that may scale better
 *  in a different DPI class.

 *
 *
Flex's default mappings are:
 *

 *
160 DPI	<140 DPI

 *
160 DPI	>=140 DPI and <=200 DPI

 *
240 DPI	>=200 DPI and <=280 DPI

 *
320 DPI	>=280 DPI and <=400 DPI

 *
480 DPI	>=400 DPI and <=560 DPI

 *
640 DPI	>=640 DPI

 *

 *

 *
 *
Subclasses of RuntimeDPIProvider should only depend on runtime APIs
 *  and should not depend on any classes specific to the Flex framework except
 *  mx.core.DPIClassification.

 *
 *  @includeExample examples/RuntimeDPIProviderApp.mxml -noswf
 *  @includeExample examples/RuntimeDPIProviderExample.as -noswf
 *  @includeExample examples/views/RuntimeDPIProviderAppView.mxml -noswf
 *
 *  @see mx.core.DPIClassification
 *  @see spark.components.Application#applicationDPI
 *
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 2.5
 *  @productversion Flex 4.5
 */
public class RuntimeDPIProvider
{
    /**
     *  Constructor.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    public function RuntimeDPIProvider()
    {
    }

    /**
     *  Returns the runtime DPI of the current device by mapping its
     *  flash.system.Capabilities.screenDPI to one of several DPI
     *  values in mx.core.DPIClassification.
     *
     *  A number of devices can have slightly different DPI values and Flex maps these
     *  into the several DPI classes.
     *
     *  Flex uses this method to calculate the current DPI value when an Application
     *  authored for a specific DPI is adapted to the current one through scaling.
     *
     *  @param dpi The DPI value.
     *  @return The corresponding DPIClassification value.
     *
     *  @see flash.system.Capabilities
     *  @see mx.core.DPIClassification
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    public function get runtimeDPI():Number
    {
        return classifyDPI(Capabilities.screenDPI);
    }

    /**
     *  @private
     *  Matches the specified DPI to a DPIClassification value.
     *  A number of devices can have slightly different DPI values and classifyDPI
     *  maps these into the several DPI classes.
     *
     *  This method is specifically kept for Design View. Flex uses RuntimeDPIProvider
     *  to calculate DPI classes.
     *
     *  @param dpi The DPI value.
     *  @return The corresponding DPIClassification value.
     */
    mx_internal static function classifyDPI(dpi:Number):Number
    {
		if (dpi <= 140)
			return DPIClassification.DPI_120;

        if (dpi <= 200)
            return DPIClassification.DPI_160;

        if (dpi <= 280)
            return DPIClassification.DPI_240;

		if (dpi <= 400)
			return DPIClassification.DPI_320;

		if (dpi <= 560)
			return DPIClassification.DPI_480;

        return DPIClassification.DPI_640;
    }
}
}

////////////////////////////////////////////////////////////////////////////////
//
//  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 mx.core
{
import flash.display.DisplayObject;
import flash.display.Stage;
import flash.system.Capabilities;

import mx.core.mx_internal;
import mx.managers.SystemManager;

use namespace mx_internal;

/**
 *  The RuntimeDPIProvider class provides the default mapping of
 *  similar device DPI values into predefined DPI classes.
 *  An Application may have its runtimeDPIProvider property set to a
 *  subclass of RuntimeDPIProvider to override Flex's default mappings.
 *  Overriding Flex's default mappings will cause changes in the Application's
 *  automatic scaling behavior.
 *
 *
Overriding Flex's default mappings is usually only necessary for devices
 *  that incorrectly report their screenDPI and for devices that may scale better
 *  in a different DPI class.

 *
 *
Flex's default mappings are:
 *

 *
160 DPI	<140 DPI

 *
160 DPI	>=140 DPI and <=200 DPI

 *
240 DPI	>=200 DPI and <=280 DPI

 *
320 DPI	>=280 DPI and <=400 DPI

 *
480 DPI	>=400 DPI and <=560 DPI

 *
640 DPI	>=640 DPI

 *

 *

 *
 *
 *
 *
Subclasses of RuntimeDPIProvider should only depend on runtime APIs
 *  and should not depend on any classes specific to the Flex framework except
 *  mx.core.DPIClassification.

 *
 *  @includeExample examples/RuntimeDPIProviderApp.mxml -noswf
 *  @includeExample examples/RuntimeDPIProviderExample.as -noswf
 *  @includeExample examples/views/RuntimeDPIProviderAppView.mxml -noswf
 *
 *  @see mx.core.DPIClassification
 *  @see spark.components.Application#applicationDPI
 *
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 2.5
 *  @productversion Flex 4.5
 */
public class RuntimeDPIProvider
{
    /**
     *  Constructor.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    public function RuntimeDPIProvider()
    {
    }

    /**
     *  Returns the runtime DPI of the current device by mapping its
     *  flash.system.Capabilities.screenDPI to one of several DPI
     *  values in mx.core.DPIClassification.
     *
     *  A number of devices can have slightly different DPI values and Flex maps these
     *  into the several DPI classes.
     *
     *  Flex uses this method to calculate the current DPI value when an Application
     *  authored for a specific DPI is adapted to the current one through scaling.
     *
     *
 Exceptions:

     *

     *
All non-retina iPads  receive 160 DPI

     *
All retina iPads  receive 320 DPI

     *

     *
     *  @param dpi The DPI value.
     *  @return The corresponding DPIClassification value.
     *                                                                               isI
     *  @see flash.system.Capabilities
     *  @see mx.core.DPIClassification
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */

    public function get runtimeDPI():Number
    {

        var isIOS:Boolean = Capabilities.version.indexOf("IOS") == 0;
        var screenDPI : Number= Capabilities.screenDPI;

        if (isIOS) {

            var root:DisplayObject = SystemManager.getSWFRoot(this);
            if (root != null )  {
                var stage:Stage = root.stage;
                if (stage != null){
                    var scX:Number = stage.fullScreenWidth;
                    var scY:Number = stage.fullScreenHeight;
                    /*  as of Dec 2013,  iPad (resp. iPad retina)   are the only iOS devices to have 1024 (resp. 2048) screen width or height
                     cf http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density#Apple
                     * */
                    if ((scX == 2048 || scY == 2048))
                        return DPIClassification.DPI_320;
                    else if (scX == 1024 || scY == 1024)
                        return DPIClassification.DPI_160;
                }
            }
        }
        return classifyDPI(screenDPI);

    }

    /**
     *  @private
     *  Matches the specified DPI to a DPIClassification value.
     *  A number of devices can have slightly different DPI values and classifyDPI
     *  maps these into the several DPI classes.
     *
     *  This method is specifically kept for Design View. Flex uses RuntimeDPIProvider
     *  to calculate DPI classes.
     *
     *  @param dpi The DPI value.
     *  @return The corresponding DPIClassification value.
     */
    mx_internal static function classifyDPI(dpi:Number):Number
    {
		if (dpi <= 140)
			return DPIClassification.DPI_120;

        if (dpi <= 200)
            return DPIClassification.DPI_160;

        if (dpi <= 280)
            return DPIClassification.DPI_240;

		if (dpi <= 400)
			return DPIClassification.DPI_320;

		if (dpi <= 560)
			return DPIClassification.DPI_480;

        return DPIClassification.DPI_640;
    }
}
}

FIX FLEX-33861 Flex Incorrectly Scaling Down Application mutella test pass: tests/mobile/*


package {

  import flash.display.Sprite;
  import flash.display.Stage;
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.display.StageDisplayState;
  import flash.events.Event;
  import flash.events.ErrorEvent;
  import flash.events.NetStatusEvent;
  import flash.events.ActivityEvent;
  import flash.events.ProgressEvent;
  import flash.events.StatusEvent;
  import flash.events.SampleDataEvent;
  import flash.events.MouseEvent;
  import flash.external.ExternalInterface;
  import flash.media.Video;
  import flash.net.NetStream;
  import flash.net.NetConnection;
  import flash.net.Socket;
  import flash.system.Security;
  import flash.utils.getTimer;

  import com.axis.rtspclient.*;
  import com.axis.audioclient.AxisTransmit;
  import com.axis.http.url;

  [SWF(frameRate="60")]
  [SWF(backgroundColor="#efefef")]

  public class Player extends Sprite {
    private var vid:Video;
    private var audioTransmit:AxisTransmit = new AxisTransmit();
    private var meta:Object = {};
    private var client:RTSPClient;
    private static var ns:NetStream;

    public function Player() {

      Security.allowDomain("*");
      Security.allowInsecureDomain("*");

      ExternalInterface.marshallExceptions = true;

      /* Media player API */
      ExternalInterface.addCallback("play", play);
      ExternalInterface.addCallback("pause", pause);
      ExternalInterface.addCallback("resume", resume);
      ExternalInterface.addCallback("stop", stop);

      /* Audio Transmission API */
      ExternalInterface.addCallback("startAudioTransmit", audioTransmitStartInterface);
      ExternalInterface.addCallback("stopAudioTransmit", audioTransmitStopInterface);

      this.stage.align = StageAlign.TOP_LEFT;
      this.stage.scaleMode = StageScaleMode.NO_SCALE;
      addEventListener(Event.ADDED_TO_STAGE, onStageAdded);

      var nc:NetConnection = new NetConnection();
      nc.connect(null);

      vid = new Video(stage.stageWidth, stage.stageHeight);

      ns = new NetStream(nc);
      ns.bufferTime = 1;
      ns.client = new Object();
      var self:Player = this;
      ns.client.onMetaData = function(item:Object):void {
        self.meta = item;
        videoResize();
      };

      this.stage.doubleClickEnabled = true;
      this.stage.addEventListener(MouseEvent.DOUBLE_CLICK, fullscreen);
      this.stage.addEventListener(Event.FULLSCREEN, function(event:Event):void {
        videoResize();
      });

      ns.play(null);
      vid.attachNetStream(ns);
      addChild(vid);
    }

    public function fullscreen(event:MouseEvent):void
    {
      this.stage.displayState = (StageDisplayState.NORMAL === stage.displayState) ?
        StageDisplayState.FULL_SCREEN : StageDisplayState.NORMAL;
    }

    public function videoResize():void
    {
      var stagewidth:uint = (StageDisplayState.NORMAL === stage.displayState) ?
        stage.stageWidth : stage.fullScreenWidth;
      var stageheight:uint = (StageDisplayState.NORMAL === stage.displayState) ?
        stage.stageHeight : stage.fullScreenHeight;

      vid.width  = Math.min(meta.width,  stagewidth);
      vid.height = Math.min(meta.height, stageheight);

      vid.x = 0;
      vid.y = 0;
      if (meta.width < stagewidth || meta.height < stageheight) {
        vid.x = (stagewidth - meta.width) / 2;
        vid.y = (stageheight - meta.height) / 2;
      }
    }

    public function play(iurl:String = null):void
    {
      var urlParsed:Object = url.parse(iurl);

      var rtspHandle:IRTSPHandle = null;
      switch (urlParsed.protocol) {
      case 'rtsph':
        /* RTSP over HTTP */
        rtspHandle = new RTSPoverHTTPHandle(urlParsed);
        break;

      case 'rtsp':
        /* RTSP over TCP */
        rtspHandle = new RTSPoverTCPHandle(urlParsed);
      }

      client = new RTSPClient(rtspHandle, urlParsed);
      rtspHandle.addEventListener('connected', function():void {
        client.start();
      });

      rtspHandle.connect();
    }

    public function pause():void
    {
      client.pause();
    }

    public function resume():void
    {
      client.resume();
    }

    public function stop():void
    {
      client.stop();
    }

    public function audioTransmitStopInterface():void {
      audioTransmit.stop();
    }

    public function audioTransmitStartInterface(url:String = null):void {
      audioTransmit.start(url);
    }

    private function onStageAdded(e:Event):void {
      trace('stage added');
    }

    public static function getNetStream():NetStream
    {
      return ns;
    }
  }
}

package {

  import flash.display.Sprite;
  import flash.display.Stage;
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.display.StageDisplayState;
  import flash.events.Event;
  import flash.events.ErrorEvent;
  import flash.events.NetStatusEvent;
  import flash.events.ActivityEvent;
  import flash.events.ProgressEvent;
  import flash.events.StatusEvent;
  import flash.events.SampleDataEvent;
  import flash.events.MouseEvent;
  import flash.external.ExternalInterface;
  import flash.media.Video;
  import flash.net.NetStream;
  import flash.net.NetConnection;
  import flash.net.Socket;
  import flash.system.Security;
  import flash.utils.getTimer;

  import com.axis.rtspclient.*;
  import com.axis.audioclient.AxisTransmit;
  import com.axis.http.url;

  [SWF(frameRate="60")]
  [SWF(backgroundColor="#efefef")]

  public class Player extends Sprite {
    private var config:Object = {
      'scaleUp' : false
    };
    private var vid:Video;
    private var audioTransmit:AxisTransmit = new AxisTransmit();
    private var meta:Object = {};
    private var client:RTSPClient;
    private static var ns:NetStream;

    public function Player() {

      Security.allowDomain("*");
      Security.allowInsecureDomain("*");

      ExternalInterface.marshallExceptions = true;

      /* Media player API */
      ExternalInterface.addCallback("play", play);
      ExternalInterface.addCallback("pause", pause);
      ExternalInterface.addCallback("resume", resume);
      ExternalInterface.addCallback("stop", stop);

      /* Audio Transmission API */
      ExternalInterface.addCallback("startAudioTransmit", audioTransmitStartInterface);
      ExternalInterface.addCallback("stopAudioTransmit", audioTransmitStopInterface);

      this.stage.align = StageAlign.TOP_LEFT;
      this.stage.scaleMode = StageScaleMode.NO_SCALE;
      addEventListener(Event.ADDED_TO_STAGE, onStageAdded);

      var nc:NetConnection = new NetConnection();
      nc.connect(null);

      vid = new Video(stage.stageWidth, stage.stageHeight);

      ns = new NetStream(nc);
      ns.bufferTime = 1;
      ns.client = new Object();
      var self:Player = this;
      ns.client.onMetaData = function(item:Object):void {
        self.meta = item;
        videoResize();
      };

      this.stage.doubleClickEnabled = true;
      this.stage.addEventListener(MouseEvent.DOUBLE_CLICK, fullscreen);
      this.stage.addEventListener(Event.FULLSCREEN, function(event:Event):void {
        videoResize();
      });

      ns.play(null);
      vid.attachNetStream(ns);
      addChild(vid);
    }

    public function fullscreen(event:MouseEvent):void
    {
      this.stage.displayState = (StageDisplayState.NORMAL === stage.displayState) ?
        StageDisplayState.FULL_SCREEN : StageDisplayState.NORMAL;
    }

    public function videoResize():void
    {
      var stagewidth:uint = (StageDisplayState.NORMAL === stage.displayState) ?
        stage.stageWidth : stage.fullScreenWidth;
      var stageheight:uint = (StageDisplayState.NORMAL === stage.displayState) ?
        stage.stageHeight : stage.fullScreenHeight;

      var scale:Number = ((stagewidth / meta.width) > (stageheight / meta.height)) ?
        (stageheight / meta.height) : (stagewidth / meta.width);

      vid.width = meta.width;
      vid.height = meta.height;
      if ((scale < 1.0) || (scale > 1.0 && true === config.scaleUp)) {
        trace('scaling video, scale:', scale.toFixed(2), ' (aspect ratio: ' +  (vid.width / vid.height).toFixed(2) + ')');
        vid.width = meta.width * scale;
        vid.height = meta.height * scale;
      }

      vid.x = (stagewidth - vid.width) / 2;
      vid.y = (stageheight - vid.height) / 2;
    }

    public function play(iurl:String = null):void
    {
      var urlParsed:Object = url.parse(iurl);

      var rtspHandle:IRTSPHandle = null;
      switch (urlParsed.protocol) {
      case 'rtsph':
        /* RTSP over HTTP */
        rtspHandle = new RTSPoverHTTPHandle(urlParsed);
        break;

      case 'rtsp':
        /* RTSP over TCP */
        rtspHandle = new RTSPoverTCPHandle(urlParsed);
      }

      client = new RTSPClient(rtspHandle, urlParsed);
      rtspHandle.addEventListener('connected', function():void {
        client.start();
      });

      rtspHandle.connect();
    }

    public function pause():void
    {
      client.pause();
    }

    public function resume():void
    {
      client.resume();
    }

    public function stop():void
    {
      client.stop();
    }

    public function audioTransmitStopInterface():void {
      audioTransmit.stop();
    }

    public function audioTransmitStartInterface(url:String = null):void {
      audioTransmit.start(url);
    }

    private function onStageAdded(e:Event):void {
      trace('stage added');
    }

    public static function getNetStream():NetStream
    {
      return ns;
    }
  }
}

scale sensibly

Player: scale sensibly

If the stage is smaller than the video, scale down the video.
If the stage is large, let a config decide if upscale should occur.
If the stage fits the video precisly do no scaling.
Afterwards, center the movie on the stage.