package com.kaltura.hls.m2ts
{
    import flash.utils.ByteArray;
    import flash.net.ObjectEncoding;
    import flash.utils.ByteArray;
    import flash.utils.Endian;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import com.hurlant.util.Hex;

    CONFIG::LOGGING
    {
        import org.osmf.logging.Logger;
        import org.osmf.logging.Log;
    }

    /**
     * Process packetized elementary streams and extract NALUs and other data.
     */
    public class PESProcessor
    {
        CONFIG::LOGGING
        {
            private static const logger:Logger = Log.getLogger("com.kaltura.hls.m2ts.PESProcessor");
        }

        public var types:Object = {};
        public var streams:Object = {};

        public var lastVideoNALU:NALU = null;

        public var transcoder:FLVTranscoder = new FLVTranscoder();

        public var headerSent:Boolean = false;

        public var pmtStreamId:int = -1;

        protected var pendingBuffers:Vector.<Object> = new Vector.<Object>();
        protected var pendingLastConvertedIndex:int = 0;

        public var lastPTS:Number = 0, lastDTS:Number = 0;

        /**
         * Given a MPEG timestamp we've seen previously, determine if the new timestamp
         * has wrapped and correct it to follow the old timestamp.
         */
        static function handleMpegTimestampWrap(newTime:Number, oldTime:Number):Number
        {
            while (!isNaN(oldTime) && (Math.abs(newTime - oldTime) > 4294967296))
                newTime += (oldTime < newTime) ? -8589934592 : 8589934592;

            return newTime;
        }

        public function logStreams():void
        {
            CONFIG::LOGGING
            {
                logger.debug("----- PES state -----");
                for(var k:* in streams)
                {
                    logger.debug("   " + k + " has " + streams[k].buffer.length + " bytes, type=" + types[k]);
                }
            }
        }

        public function clear(clearAACConfig:Boolean = true):void
        {
            streams = {};
            lastVideoNALU = null;
            transcoder.clear(clearAACConfig);

            // Reset PTS/DTS reference.
            lastPTS = lastDTS = 0;
        }

        private function parseProgramAssociationTable(bytes:ByteArray, cursor:uint):Boolean
        {
            // Get the section length.
            var sectionLen:uint = ((bytes[cursor+2] & 0x03) << 8) | bytes[cursor+3];

            // Check the section length for a single PMT.
            CONFIG::LOGGING
            {
                if (sectionLen > 13)
                {
                    logger.debug("Saw multiple PMT entries in the PAT; blindly choosing first one.");
                }
            }

            // Grab the PMT ID.
            pmtStreamId = ((bytes[cursor+10] << 8) | bytes[cursor+11]) & 0x1FFF;

            CONFIG::LOGGING
            {
                logger.debug("Saw PMT ID of " + pmtStreamId);
            }

            return true;
        }

        private function parseProgramMapTable(bytes:ByteArray, cursor:uint):Boolean
        {
            var sectionLength:uint;
            var sectionLimit:uint;
            var programInfoLength:uint;
            var type:uint;
            var pid:uint;
            var esInfoLength:uint;
            var seenPIDsByClass:Array;
            var mediaClass:int;

            var hasAudio:Boolean = false, hasVideo:Boolean = false;

            // Set up types.
            types = [];
            seenPIDsByClass = [];
            seenPIDsByClass[MediaClass.VIDEO] = Infinity;
            seenPIDsByClass[MediaClass.AUDIO] = Infinity;

            // Process section length and limit.
            cursor++;

            sectionLength = ((bytes[cursor] & 0x0f) << 8) + bytes[cursor + 1];
            cursor += 2;

            if(sectionLength + cursor > bytes.length)
            {
                CONFIG::LOGGING
                {
                    logger.error("Not enough data to read. 1");
                }
                return false;
            }

            // Skip a few things we don't care about: program number, RSV, version, CNI, section, last_section, pcr_cid
            sectionLimit = cursor + sectionLength;
            cursor += 7;

            // And get the program info length.
            programInfoLength = ((bytes[cursor] & 0x0f) << 8) + bytes[cursor + 1];
            cursor += 2;

            // If not enough data to proceed, bail.
            if(programInfoLength + cursor > bytes.length)
            {
                CONFIG::LOGGING
                {
                    logger.error("Not enough data to read. 2");
                }
                return false;
            }

            cursor += programInfoLength;

            const CRC_SIZE:int = 4;
            while(cursor < sectionLimit - CRC_SIZE)
            {
                type = bytes[cursor++];
                pid = ((bytes[cursor] & 0x1f) << 8) + bytes[cursor + 1];
                cursor += 2;

                mediaClass = MediaClass.calculate(type);

                if(mediaClass == MediaClass.VIDEO)
                    hasVideo = true

                if(mediaClass == MediaClass.AUDIO)
                    hasAudio = true

                // For video & audio, select the lowest PID for each kind.
                if(mediaClass == MediaClass.OTHER
                 || pid < seenPIDsByClass[mediaClass])
                {
                    // Clear a higher PID if present.
                    if(mediaClass != MediaClass.OTHER
                     && seenPIDsByClass[mediaClass] < Infinity)
                        types[seenPIDsByClass[mediaClass]] = -1;

                    types[pid] = type;
                    seenPIDsByClass[mediaClass] = pid;
                }

                // Skip the esInfo data.
                esInfoLength = ((bytes[cursor] & 0x0f) << 8) + bytes[cursor + 1];
                cursor += 2;
                cursor += esInfoLength;
            }

            // Cook out header to transcoder.
            //transcoder.writeHeader(hasAudio, hasVideo);
            headerSent = true;

            return true;
        }

        public function append(packet:PESPacket):Boolean
        {
//            logger.debug("saw packet of " + packet.buffer.length);
            var b:ByteArray = packet.buffer;
            b.position = 0;

            if(b.length < 8)
            {
                CONFIG::LOGGING
                {
                    logger.error("Ignoring too short PES packet, length=" + b.length);
                }
                return true;
            }

            // Get the start code.
            var startCode:uint = b.readUnsignedInt();
            if((startCode & 0xFFFFFF00) != 0x00000100)
            {
                // It could be a program association table.
                if((startCode & 0xFFFFFF00) == 0x0000b000)
                {
                    parseProgramAssociationTable(b, 1);
                    return true;
                }

                // It could be the program map table.
                if((startCode & 0xFFFFFC00) == 0x0002b000)
                {
                    parseProgramMapTable(b, 1);
                    return true;
                }

                var tmp:ByteArray = new ByteArray();
                tmp.writeInt(startCode);
                CONFIG::LOGGING
                {
                    logger.error("ES prefix was wrong, expected 00:00:01:xx but got " + Hex.fromArray(tmp, true));
                }
                return true;
            }

            // Get the stream ID.
            var streamID:int = startCode & 0xFF;

            // Get the length.
            var packetLength:uint = b.readUnsignedShort();
            if(packetLength)
            {
                if(b.length < packetLength )
                {
                    CONFIG::LOGGING
                    {
                        logger.warn("WARNING: parsePESPacket - not enough bytes, expecting " + packetLength + ", but have " + b.length);
                    }
                    return false; // not enough bytes in packet
                }
            }

            if(b.length < 9)
            {
                CONFIG::LOGGING
                {
                    logger.warn("WARNING: parsePESPacket - too short to read header!");
                }
                return false;
            }

            // Read the rest of the header.
            var cursor:uint = 6;
            var dataAlignment:Boolean = (b[cursor] & 0x04) != 0;
            cursor++;

            var ptsDts:uint = (b[cursor] & 0xc0) >> 6;
            cursor++;

            var pesHeaderDataLength:uint = b[cursor];
            cursor++;

            //logger.debug(" PES align=" + dataAlignment + " ptsDts=" + ptsDts + " header=" + pesHeaderDataLength);

            var pts:Number = 0, dts:Number = 0;

            if(ptsDts & 0x02)
            {
                // has PTS at least
                if(cursor + 5 > b.length)
                    return true;

                pts  = b[cursor] & 0x0e;
                pts *= 128;
                pts += b[cursor + 1];
                pts *= 256;
                pts += b[cursor + 2] & 0xfe;
                pts *= 128;
                pts += b[cursor + 3];
                pts *= 256;
                pts += b[cursor + 4] & 0xfe;
                pts /= 2;

                if(ptsDts & 0x01)
                {
                    // DTS too!
                    if(cursor + 10 > b.length)
                        return true;

                    dts  = b[cursor + 5] & 0x0e;
                    dts *= 128;
                    dts += b[cursor + 6];
                    dts *= 256;
                    dts += b[cursor + 7] & 0xfe;
                    dts *= 128;
                    dts += b[cursor + 8];
                    dts *= 256;
                    dts += b[cursor + 9] & 0xfe;
                    dts /= 2;
                }
                else
                {
                    //logger.debug("Filling in DTS")
                    dts = pts;
                }
            }

            // Condition PTS and DTS.
            pts = handleMpegTimestampWrap(pts, lastPTS);
            lastPTS = pts;

            dts = handleMpegTimestampWrap(dts, lastDTS);
            lastDTS = dts;

            packet.pts = pts;
            packet.dts = dts;
            //logger.debug("   PTS=" + pts/90000 + " DTS=" + dts/90000);

            cursor += pesHeaderDataLength;

            if(cursor > b.length)
            {
                CONFIG::LOGGING
                {
                    logger.warn("WARNING: parsePESPacket - ran out of bytes");
                }
                return true;
            }

            if(types[packet.packetID] == undefined)
            {
                CONFIG::LOGGING
                {
                    logger.warn("WARNING: parsePESPacket - unknown type");
                }
                return true;
            }

            var pes:PESPacketStream;

            if(streams[packet.packetID] == undefined)
            {
                if(dts < 0.0)
                {
                    CONFIG::LOGGING
                    {
                        logger.warn("WARNING: parsePESPacket - invalid decode timestamp, skipping");
                    }
                    return true;
                }

                pes = new PESPacketStream();
                streams[packet.packetID] = pes;
            }
            else
            {
                pes = streams[packet.packetID];
            }

            if(headerSent == false)
            {
                CONFIG::LOGGING
                {
                    logger.warn("Skipping data that came before PMT");
                }
                return true;
            }

            // Note the type at this moment in time.
            packet.type = types[packet.packetID];
            packet.headerLength = cursor;

            // And process.
            if(MediaClass.calculate(types[packet.packetID]) == MediaClass.VIDEO)
            {
                var start:int = NALU.scan(b, cursor, true);
                if(start == -1 && lastVideoNALU)
                {
                    CONFIG::LOGGING
                    {
                        logger.debug("Stuff entire " + (b.length - cursor) + " bytes into previous NALU.");
                    }
                    lastVideoNALU.buffer.position = lastVideoNALU.buffer.length;
                    b.position = 0;
                    lastVideoNALU.buffer.writeBytes(b, cursor, b.length - cursor);
                    return true;

                }
                else if((start - cursor) > 0 && lastVideoNALU)
                {
                    // Shove into previous buffer.
                    CONFIG::LOGGING
                    {
                        logger.debug("Stuffing first " + (start - cursor) + " bytes into previous NALU.");
                    }
                    lastVideoNALU.buffer.position = lastVideoNALU.buffer.length;
                    b.position = 0;
                    lastVideoNALU.buffer.writeBytes(b, cursor, start - cursor);
                    cursor = start;
                }

                // If it's identical timestamps, accumulate it into the current unit and keep going.
                if(lastVideoNALU && pts == lastVideoNALU.pts && dts == lastVideoNALU.dts)
                {
                    CONFIG::LOGGING
                    {
                        logger.debug("Combining " + (start-cursor) + " bytes into previous NALU due to matching DTS/PTS.");
                    }
                    lastVideoNALU.buffer.position = lastVideoNALU.buffer.length;
                    b.position = 0;
                    lastVideoNALU.buffer.writeBytes(b, cursor, start - cursor);
                    cursor = start;
                }
                else
                {
                    // Submit previous data.
                    if(lastVideoNALU)
                    {
                        pendingBuffers.push(lastVideoNALU.clone());
                    }

                    // Update NALU state.
                    lastVideoNALU = new NALU();
                    lastVideoNALU.buffer = new ByteArray();
                    lastVideoNALU.pts = pts;
                    lastVideoNALU.dts = dts;
                    lastVideoNALU.type = packet.type;
                    lastVideoNALU.buffer.writeBytes(b, cursor);

                }
            }
            else if(types[packet.packetID] == 0x0F)
            {
                // It's an AAC stream.
                pendingBuffers.push(packet.clone());
            }
            else if(types[packet.packetID] == 0x03 || types[packet.packetID] == 0x04)
            {
                // It's an MP3 stream.
                pendingBuffers.push(packet.clone());
            }
            else
            {
                CONFIG::LOGGING
                {
                    logger.warn("Unknown packet ID type " + types[packet.packetID] + ", ignoring (A).");
                }
            }

            bufferPendingNalus();

            return true;
        }

        /**
         * To avoid transcoding all content on flush, we buffer it into FLV
         * tags as we go. However, they remain undelivered until we can gather
         * final SPS/PPS information. This method is responsible for
         * incrementally buffering in the FLV transcoder as we go.
         */
        public function bufferPendingNalus():void
        {
            // Iterate and buffer new NALUs.
            for(var i:int=pendingLastConvertedIndex; i<pendingBuffers.length; i++)
            {
                if(pendingBuffers[i] is NALU)
                {
                    transcoder.convert(pendingBuffers[i] as NALU);
                }
                else if(pendingBuffers[i] is PESPacket)
                {
                    var packet:PESPacket = pendingBuffers[i] as PESPacket;
                    if(packet.type == 0x0F)
                    {
                        // It's an AAC stream.
                        transcoder.convertAAC(packet);
                    }
                    else if(packet.type == 0x03 || packet.type == 0x04)
                    {
                        // It's an MP3 stream. Pass through directly.
                        transcoder.convertMP3(packet);
                    }
                    else
                    {
                        CONFIG::LOGGING
                        {
                            logger.warn("Unknown packet ID type " + packet.type + ", ignoring (B).");
                        }
                    }
                }
            }

            // Note the last item we converted so we can avoid duplicating work.
            pendingLastConvertedIndex = pendingBuffers.length;
        }

        public function processAllNalus():void
        {
            // Consume any unposted video NALUs.
            if(lastVideoNALU)
            {
                pendingBuffers.push(lastVideoNALU.clone());
                lastVideoNALU = null;
            }

            // First walk all the video NALUs and get the correct SPS/PPS
            if(pendingBuffers.length == 0)
                return;

            // Then emit SPS/PPS
            transcoder.emitSPSPPSUnbuffered();

            // Complete buffering and emit it all.
            bufferPendingNalus();
            transcoder.emitBufferedTags();

            // Don't forget to clear the pending list.
            pendingBuffers.length = 0;
            pendingLastConvertedIndex = 0;
        }
    }
}

package com.kaltura.hls.m2ts
{
    import flash.utils.ByteArray;
    import flash.net.ObjectEncoding;
    import flash.utils.ByteArray;
    import flash.utils.Endian;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import com.hurlant.util.Hex;

    CONFIG::LOGGING
    {
        import org.osmf.logging.Logger;
        import org.osmf.logging.Log;
    }

    /**
     * Process packetized elementary streams and extract NALUs and other data.
     */
    public class PESProcessor
    {
        CONFIG::LOGGING
        {
            private static const logger:Logger = Log.getLogger("com.kaltura.hls.m2ts.PESProcessor");
        }

        public var types:Object = {};
        public var streams:Object = {};

        public var lastVideoNALU:NALU = null;

        public var transcoder:FLVTranscoder = new FLVTranscoder();

        public var headerSent:Boolean = false;

        public var pmtStreamId:int = -1;

        protected var pendingBuffers:Vector.<Object> = new Vector.<Object>();
        protected var pendingLastConvertedIndex:int = 0;

        public var lastPTS:Number = 0, lastDTS:Number = 0;

        /**
         * Given a MPEG timestamp we've seen previously, determine if the new timestamp
         * has wrapped and correct it to follow the old timestamp.
         */
        static function handleMpegTimestampWrap(newTime:Number, oldTime:Number):Number
        {
            while (!isNaN(oldTime) && (Math.abs(newTime - oldTime) > 4294967296))
                newTime += (oldTime < newTime) ? -8589934592 : 8589934592;

            return newTime;
        }

        public function logStreams():void
        {
            CONFIG::LOGGING
            {
                logger.debug("----- PES state -----");
                for(var k:* in streams)
                {
                    logger.debug("   " + k + " has " + streams[k].buffer.length + " bytes, type=" + types[k]);
                }
            }
        }

        public function clear(clearAACConfig:Boolean = true):void
        {
            streams = {};
            lastVideoNALU = null;
            transcoder.clear(clearAACConfig);

            // Reset PTS/DTS reference.
            lastPTS = lastDTS = 0;
        }

        private function parseProgramAssociationTable(bytes:ByteArray, cursor:uint):Boolean
        {
            // Get the section length.
            var sectionLen:uint = ((bytes[cursor+2] & 0x03) << 8) | bytes[cursor+3];

            // Check the section length for a single PMT.
            CONFIG::LOGGING
            {
                if (sectionLen > 13)
                {
                    logger.debug("Saw multiple PMT entries in the PAT; blindly choosing first one.");
                }
            }

            // Grab the PMT ID.
            pmtStreamId = ((bytes[cursor+10] << 8) | bytes[cursor+11]) & 0x1FFF;

            CONFIG::LOGGING
            {
                logger.debug("Saw PMT ID of " + pmtStreamId);
            }

            return true;
        }

        private function parseProgramMapTable(bytes:ByteArray, cursor:uint):Boolean
        {
            var sectionLength:uint;
            var sectionLimit:uint;
            var programInfoLength:uint;
            var type:uint;
            var pid:uint;
            var esInfoLength:uint;
            var seenPIDsByClass:Array;
            var mediaClass:int;

            var hasAudio:Boolean = false, hasVideo:Boolean = false;

            // Set up types.
            types = [];
            seenPIDsByClass = [];
            seenPIDsByClass[MediaClass.VIDEO] = Infinity;
            seenPIDsByClass[MediaClass.AUDIO] = Infinity;

            // Process section length and limit.
            cursor++;

            sectionLength = ((bytes[cursor] & 0x0f) << 8) + bytes[cursor + 1];
            cursor += 2;

            if(sectionLength + cursor > bytes.length)
            {
                CONFIG::LOGGING
                {
                    logger.error("Not enough data to read. 1");
                }
                return false;
            }

            // Skip a few things we don't care about: program number, RSV, version, CNI, section, last_section, pcr_cid
            sectionLimit = cursor + sectionLength;
            cursor += 7;

            // And get the program info length.
            programInfoLength = ((bytes[cursor] & 0x0f) << 8) + bytes[cursor + 1];
            cursor += 2;

            // If not enough data to proceed, bail.
            if(programInfoLength + cursor > bytes.length)
            {
                CONFIG::LOGGING
                {
                    logger.error("Not enough data to read. 2");
                }
                return false;
            }

            cursor += programInfoLength;

            const CRC_SIZE:int = 4;
            while(cursor < sectionLimit - CRC_SIZE)
            {
                type = bytes[cursor++];
                pid = ((bytes[cursor] & 0x1f) << 8) + bytes[cursor + 1];
                cursor += 2;

                mediaClass = MediaClass.calculate(type);

                if(mediaClass == MediaClass.VIDEO)
                    hasVideo = true

                if(mediaClass == MediaClass.AUDIO)
                    hasAudio = true

                // For video & audio, select the lowest PID for each kind.
                if(mediaClass == MediaClass.OTHER
                 || pid < seenPIDsByClass[mediaClass])
                {
                    // Clear a higher PID if present.
                    if(mediaClass != MediaClass.OTHER
                     && seenPIDsByClass[mediaClass] < Infinity)
                        types[seenPIDsByClass[mediaClass]] = -1;

                    types[pid] = type;
                    seenPIDsByClass[mediaClass] = pid;
                }

                // Skip the esInfo data.
                esInfoLength = ((bytes[cursor] & 0x0f) << 8) + bytes[cursor + 1];
                cursor += 2;
                cursor += esInfoLength;
            }

            // Cook out header to transcoder.
            //transcoder.writeHeader(hasAudio, hasVideo);
            headerSent = true;

            return true;
        }

        public function append(packet:PESPacket):Boolean
        {
//            logger.debug("saw packet of " + packet.buffer.length);
            var b:ByteArray = packet.buffer;
            b.position = 0;

            if(b.length < 8)
            {
                CONFIG::LOGGING
                {
                    logger.error("Ignoring too short PES packet, length=" + b.length);
                }
                return true;
            }

            // Get the start code.
            var startCode:uint = b.readUnsignedInt();
            if((startCode & 0xFFFFFF00) != 0x00000100)
            {
                // It could be a program association table.
                if((startCode & 0xFFFFFF00) == 0x0000b000)
                {
                    parseProgramAssociationTable(b, 1);
                    return true;
                }

                // It could be the program map table.
                if((startCode & 0xFFFFFC00) == 0x0002b000)
                {
                    parseProgramMapTable(b, 1);
                    return true;
                }

                var tmp:ByteArray = new ByteArray();
                tmp.writeInt(startCode);
                CONFIG::LOGGING
                {
                    logger.error("ES prefix was wrong, expected 00:00:01:xx but got " + Hex.fromArray(tmp, true));
                }
                return true;
            }

            // Get the stream ID.
            var streamID:int = startCode & 0xFF;

            // Get the length.
            var packetLength:uint = b.readUnsignedShort();
            if(packetLength)
            {
                if(b.length < packetLength )
                {
                    CONFIG::LOGGING
                    {
                        logger.warn("WARNING: parsePESPacket - not enough bytes, expecting " + packetLength + ", but have " + b.length);
                    }
                    return false; // not enough bytes in packet
                }
            }

            if(b.length < 9)
            {
                CONFIG::LOGGING
                {
                    logger.warn("WARNING: parsePESPacket - too short to read header!");
                }
                return false;
            }

            // Read the rest of the header.
            var cursor:uint = 6;
            var dataAlignment:Boolean = (b[cursor] & 0x04) != 0;
            cursor++;

            var ptsDts:uint = (b[cursor] & 0xc0) >> 6;
            cursor++;

            var pesHeaderDataLength:uint = b[cursor];
            cursor++;

            //logger.debug(" PES align=" + dataAlignment + " ptsDts=" + ptsDts + " header=" + pesHeaderDataLength);

            var pts:Number = 0, dts:Number = 0;

            if(ptsDts & 0x02)
            {
                // has PTS at least
                if(cursor + 5 > b.length)
                    return true;

                pts  = b[cursor] & 0x0e;
                pts *= 128;
                pts += b[cursor + 1];
                pts *= 256;
                pts += b[cursor + 2] & 0xfe;
                pts *= 128;
                pts += b[cursor + 3];
                pts *= 256;
                pts += b[cursor + 4] & 0xfe;
                pts /= 2;

                if(ptsDts & 0x01)
                {
                    // DTS too!
                    if(cursor + 10 > b.length)
                        return true;

                    dts  = b[cursor + 5] & 0x0e;
                    dts *= 128;
                    dts += b[cursor + 6];
                    dts *= 256;
                    dts += b[cursor + 7] & 0xfe;
                    dts *= 128;
                    dts += b[cursor + 8];
                    dts *= 256;
                    dts += b[cursor + 9] & 0xfe;
                    dts /= 2;
                }
                else
                {
                    //logger.debug("Filling in DTS")
                    dts = pts;
                }
            }

            // Condition PTS and DTS.
            pts = handleMpegTimestampWrap(pts, lastPTS);
            lastPTS = pts;

            dts = handleMpegTimestampWrap(dts, lastDTS);
            lastDTS = dts;

            packet.pts = pts;
            packet.dts = dts;
            //logger.debug("   PTS=" + pts/90000 + " DTS=" + dts/90000);

            cursor += pesHeaderDataLength;

            if(cursor > b.length)
            {
                CONFIG::LOGGING
                {
                    logger.warn("WARNING: parsePESPacket - ran out of bytes");
                }
                return true;
            }

            if(types[packet.packetID] == undefined)
            {
                CONFIG::LOGGING
                {
                    logger.warn("WARNING: parsePESPacket - unknown type");
                }
                return true;
            }

            var pes:PESPacketStream;

            if(streams[packet.packetID] == undefined)
            {
                if(dts < 0.0)
                {
                    CONFIG::LOGGING
                    {
                        logger.warn("WARNING: parsePESPacket - invalid decode timestamp? DTS="  + dts);
                    }
                    //return true;
                }

                pes = new PESPacketStream();
                streams[packet.packetID] = pes;
            }
            else
            {
                pes = streams[packet.packetID];
            }

            if(headerSent == false)
            {
                CONFIG::LOGGING
                {
                    logger.warn("Skipping data that came before PMT");
                }
                return true;
            }

            // Note the type at this moment in time.
            packet.type = types[packet.packetID];
            packet.headerLength = cursor;

            // And process.
            if(MediaClass.calculate(types[packet.packetID]) == MediaClass.VIDEO)
            {
                var start:int = NALU.scan(b, cursor, true);
                if(start == -1 && lastVideoNALU)
                {
                    CONFIG::LOGGING
                    {
                        logger.debug("Stuff entire " + (b.length - cursor) + " bytes into previous NALU.");
                    }
                    lastVideoNALU.buffer.position = lastVideoNALU.buffer.length;
                    b.position = 0;
                    lastVideoNALU.buffer.writeBytes(b, cursor, b.length - cursor);
                    return true;

                }
                else if((start - cursor) > 0 && lastVideoNALU)
                {
                    // Shove into previous buffer.
                    CONFIG::LOGGING
                    {
                        logger.debug("Stuffing first " + (start - cursor) + " bytes into previous NALU.");
                    }
                    lastVideoNALU.buffer.position = lastVideoNALU.buffer.length;
                    b.position = 0;
                    lastVideoNALU.buffer.writeBytes(b, cursor, start - cursor);
                    cursor = start;
                }

                // If it's identical timestamps, accumulate it into the current unit and keep going.
                if(lastVideoNALU && pts == lastVideoNALU.pts && dts == lastVideoNALU.dts)
                {
                    CONFIG::LOGGING
                    {
                        logger.debug("Combining " + (start-cursor) + " bytes into previous NALU due to matching DTS/PTS.");
                    }
                    lastVideoNALU.buffer.position = lastVideoNALU.buffer.length;
                    b.position = 0;
                    lastVideoNALU.buffer.writeBytes(b, cursor, start - cursor);
                    cursor = start;
                }
                else
                {
                    // Submit previous data.
                    if(lastVideoNALU)
                    {
                        pendingBuffers.push(lastVideoNALU.clone());
                    }

                    // Update NALU state.
                    lastVideoNALU = new NALU();
                    lastVideoNALU.buffer = new ByteArray();
                    lastVideoNALU.pts = pts;
                    lastVideoNALU.dts = dts;
                    lastVideoNALU.type = packet.type;
                    lastVideoNALU.buffer.writeBytes(b, cursor);

                }
            }
            else if(types[packet.packetID] == 0x0F)
            {
                // It's an AAC stream.
                pendingBuffers.push(packet.clone());
            }
            else if(types[packet.packetID] == 0x03 || types[packet.packetID] == 0x04)
            {
                // It's an MP3 stream.
                pendingBuffers.push(packet.clone());
            }
            else
            {
                CONFIG::LOGGING
                {
                    logger.warn("Unknown packet ID type " + types[packet.packetID] + ", ignoring (A).");
                }
            }

            bufferPendingNalus();

            return true;
        }

        /**
         * To avoid transcoding all content on flush, we buffer it into FLV
         * tags as we go. However, they remain undelivered until we can gather
         * final SPS/PPS information. This method is responsible for
         * incrementally buffering in the FLV transcoder as we go.
         */
        public function bufferPendingNalus():void
        {
            // Iterate and buffer new NALUs.
            for(var i:int=pendingLastConvertedIndex; i<pendingBuffers.length; i++)
            {
                if(pendingBuffers[i] is NALU)
                {
                    transcoder.convert(pendingBuffers[i] as NALU);
                }
                else if(pendingBuffers[i] is PESPacket)
                {
                    var packet:PESPacket = pendingBuffers[i] as PESPacket;
                    if(packet.type == 0x0F)
                    {
                        // It's an AAC stream.
                        transcoder.convertAAC(packet);
                    }
                    else if(packet.type == 0x03 || packet.type == 0x04)
                    {
                        // It's an MP3 stream. Pass through directly.
                        transcoder.convertMP3(packet);
                    }
                    else
                    {
                        CONFIG::LOGGING
                        {
                            logger.warn("Unknown packet ID type " + packet.type + ", ignoring (B).");
                        }
                    }
                }
            }

            // Note the last item we converted so we can avoid duplicating work.
            pendingLastConvertedIndex = pendingBuffers.length;
        }

        public function processAllNalus():void
        {
            // Consume any unposted video NALUs.
            if(lastVideoNALU)
            {
                pendingBuffers.push(lastVideoNALU.clone());
                lastVideoNALU = null;
            }

            // First walk all the video NALUs and get the correct SPS/PPS
            if(pendingBuffers.length == 0)
                return;

            // Then emit SPS/PPS
            transcoder.emitSPSPPSUnbuffered();

            // Complete buffering and emit it all.
            bufferPendingNalus();
            transcoder.emitBufferedTags();

            // Don't forget to clear the pending list.
            pendingBuffers.length = 0;
            pendingLastConvertedIndex = 0;
        }
    }
}

Fix issue with unwrapped negative DTS dropping PES packets.


package aerys.minko.scene.node.camera
{
	import aerys.minko.scene.controller.camera.CameraController;
	import aerys.minko.scene.data.CameraDataProvider;
	import aerys.minko.scene.node.AbstractSceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Ray;

	public class AbstractCamera extends AbstractSceneNode
	{
		public static const DEFAULT_ZNEAR	: Number	= .1;
		public static const DEFAULT_ZFAR	: Number	= 500.;

		protected var _cameraData	: CameraDataProvider	= null;

		protected var _enabled		: Boolean				= true;

		protected var _activated	: Signal				= new Signal('Camera.activated');
		protected var _deactivated	: Signal				= new Signal('Camera.deactivated');

		public function get cameraData() : CameraDataProvider
		{
			return _cameraData;
		}

		public function get zNear() : Number
		{
			return _cameraData.zNear;
		}
		public function set zNear(value : Number) : void
		{
			_cameraData.zNear = value;
		}

		public function get zFar() : Number
		{
			return _cameraData.zFar;
		}
		public function set zFar(value : Number) : void
		{
			_cameraData.zFar = value;
		}

		public function get enabled() : Boolean
		{
			return _enabled;
		}
		public function set enabled(value : Boolean) : void
		{
			if (value != _enabled)
			{
				_enabled = value;

				if (_enabled)
					_activated.execute(this);
				else
					_deactivated.execute(this);
			}
		}

		public function get activated() : Signal
		{
			return _activated;
		}
		public function get deactivated() : Signal
		{
			return _deactivated;
		}

		public function get worldToView() : Matrix4x4
		{
			return worldToLocal;
		}

		public function get viewToWorld() : Matrix4x4
		{
			return localToWorld;
		}

		public function get worldToScreen() : Matrix4x4
		{
			return _cameraData.worldToScreen;
		}

		public function AbstractCamera(zNear	: Number = DEFAULT_ZNEAR,
									   zFar		: Number = DEFAULT_ZFAR)
		{
			super();

			_cameraData = new CameraDataProvider(worldToView, viewToWorld);

			_cameraData.zNear 	= zNear;
			_cameraData.zFar 	= zFar;

			initialize();
		}

		protected function initialize() : void
		{
			throw new Error('Must be overriden.');
		}

		public function unproject(x : Number, v : Number, out : Ray = null) : Ray
		{
			throw new Error('Must be overriden.');
		}

		public function getWorldToScreen(output : Matrix4x4) : Matrix4x4
		{
			return output != null
				? output.copyFrom(_cameraData.worldToScreen)
				: _cameraData.worldToScreen.clone();
		}

		public function getScreenToWorld(output : Matrix4x4) : Matrix4x4
		{
			return output != null
				? output.copyFrom(_cameraData.screenToWorld)
				: _cameraData.screenToWorld.clone();
		}

		public function getProjection(output : Matrix4x4) : Matrix4x4
		{
			return output != null
				? output.copyFrom(_cameraData.projection)
				: _cameraData.projection.clone();
		}
	}
}

package aerys.minko.scene.node.camera
{
	import aerys.minko.scene.controller.camera.CameraController;
	import aerys.minko.scene.data.CameraDataProvider;
	import aerys.minko.scene.node.AbstractSceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Ray;

	public class AbstractCamera extends AbstractSceneNode
	{
		public static const DEFAULT_ZNEAR	: Number	= .1;
		public static const DEFAULT_ZFAR	: Number	= 500.;

		protected var _cameraData	: CameraDataProvider	= null;

		protected var _enabled		: Boolean				= true;

		protected var _activated	: Signal				= new Signal('Camera.activated');
		protected var _deactivated	: Signal				= new Signal('Camera.deactivated');

		public function get cameraData() : CameraDataProvider
		{
			return _cameraData;
		}

		public function get zNear() : Number
		{
			return _cameraData.zNear;
		}
		public function set zNear(value : Number) : void
		{
			_cameraData.zNear = value;
		}

		public function get zFar() : Number
		{
			return _cameraData.zFar;
		}
		public function set zFar(value : Number) : void
		{
			_cameraData.zFar = value;
		}

		public function get enabled() : Boolean
		{
			return _enabled;
		}
		public function set enabled(value : Boolean) : void
		{
			if (value != _enabled)
			{
				_enabled = value;

				if (_enabled)
					_activated.execute(this);
				else
					_deactivated.execute(this);
			}
		}

		public function get activated() : Signal
		{
			return _activated;
		}
		public function get deactivated() : Signal
		{
			return _deactivated;
		}

		public function get worldToView() : Matrix4x4
		{
			return worldToLocal;
		}

		public function get viewToWorld() : Matrix4x4
		{
			return localToWorld;
		}

		public function get worldToScreen() : Matrix4x4
		{
			return _cameraData.worldToScreen;
		}

		public function get projection() : Matrix4x4
		{
			return _cameraData.projection;
		}

		public function AbstractCamera(zNear	: Number = DEFAULT_ZNEAR,
									   zFar		: Number = DEFAULT_ZFAR)
		{
			super();

			_cameraData = new CameraDataProvider(worldToView, viewToWorld);

			_cameraData.zNear 	= zNear;
			_cameraData.zFar 	= zFar;

			initialize();
		}

		protected function initialize() : void
		{
			throw new Error('Must be overriden.');
		}

		public function unproject(x : Number, v : Number, out : Ray = null) : Ray
		{
			throw new Error('Must be overriden.');
		}
	}
}

add Camera.projection property and remove duplicates


/**
 * User: DoubleFx Date: 30/04/2014 Time: 17:34
 */
package mx.core {
import flash.display.Sprite;
import flash.system.Capabilities;
import flash.utils.setInterval;

/**
 *  DebuggableWorker should be used as a base class
 *  for workers instead of Sprite.
 *  it allows the debugging of those workers using FDB.
 *
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 3.4
 *  @productversion Flex 4
 */
public class DebuggableWorker extends Sprite {

    include "../core/Version.as";

    public function DebuggableWorker() {

        // Stick a timer here so that we will execute script every 1.5s
        // no matter what.
        // This is strictly for the debugger to be able to halt.
        // Note: isDebugger is true only with a Debugger Player.
        if (Capabilities.isDebugger == true) {
            setInterval(debugTickler, 1500);
        }
    }

    /**
     *  @private
     *  This is here so we get the this pointer set to Application.
     */
    private function debugTickler():void {
        // We need some bytes of code in order to have a place to break.
        var i:int = 0;
    }
}
}

////////////////////////////////////////////////////////////////////////////////
//
//  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.Sprite;
import flash.system.Capabilities;
import flash.utils.setInterval;

/**
 *  DebuggableWorker should be used as a base class
 *  for workers instead of Sprite.
 *  it allows the debugging of those workers using FDB.
 *
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 3.4
 *  @productversion Flex 4
 */
public class DebuggableWorker extends Sprite {

    include "../core/Version.as";

    public function DebuggableWorker() {

        // Stick a timer here so that we will execute script every 1.5s
        // no matter what.
        // This is strictly for the debugger to be able to halt.
        // Note: isDebugger is true only with a Debugger Player.
        if (Capabilities.isDebugger == true) {
            setInterval(debugTickler, 1500);
        }
    }

    /**
     *  @private
     *  This is here so we get the this pointer set to Application.
     */
    private function debugTickler():void {
        // We need some bytes of code in order to have a place to break.
        var i:int = 0;
    }
}
}

FLEX-34294: Create a base Class for workers making them debuggable via FDB
- Added the missing Apache Header


package com.kaltura.hls.m2ts
{
    import flash.utils.ByteArray;
    import flash.net.ObjectEncoding;
    import flash.utils.ByteArray;
    import flash.utils.Endian;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import com.hurlant.util.Hex;

    CONFIG::LOGGING
    {
        import org.osmf.logging.Logger;
        import org.osmf.logging.Log;
    }

    /**
     * Process packetized elementary streams and extract NALUs and other data.
     */
    public class PESProcessor
    {
        CONFIG::LOGGING
        {
            private static const logger:Logger = Log.getLogger("com.kaltura.hls.m2ts.PESProcessor");
        }

        public var types:Object = {};
        public var streams:Object = {};

        public var lastVideoNALU:NALU = null;

        public var transcoder:FLVTranscoder = new FLVTranscoder();

        public var headerSent:Boolean = false;

        public var pmtStreamId:int = -1;

        protected var pendingBuffers:Vector.<Object> = new Vector.<Object>();
        protected var pendingLastConvertedIndex:int = 0;

        public var lastPTS:Number = 0, lastDTS:Number = 0;

        /**
         * Given a MPEG timestamp we've seen previously, determine if the new timestamp
         * has wrapped and correct it to follow the old timestamp.
         */
        static function handleMpegTimestampWrap(newTime:Number, oldTime:Number):Number
        {
            while (!isNaN(oldTime) && (Math.abs(newTime - oldTime) > 4294967296))
                newTime += (oldTime < newTime) ? -8589934592 : 8589934592;

            return newTime;
        }

        public function logStreams():void
        {
            CONFIG::LOGGING
            {
                logger.debug("----- PES state -----");
                for(var k:* in streams)
                {
                    logger.debug("   " + k + " has " + streams[k].buffer.length + " bytes, type=" + types[k]);
                }
            }
        }

        public function clear(clearAACConfig:Boolean = true):void
        {
            streams = {};
            lastVideoNALU = null;
            transcoder.clear(clearAACConfig);
        }

        private function parseProgramAssociationTable(bytes:ByteArray, cursor:uint):Boolean
        {
            // Get the section length.
            var sectionLen:uint = ((bytes[cursor+2] & 0x03) << 8) | bytes[cursor+3];

            // Check the section length for a single PMT.
            CONFIG::LOGGING
            {
                if (sectionLen > 13)
                {
                    logger.debug("Saw multiple PMT entries in the PAT; blindly choosing first one.");
                }
            }

            // Grab the PMT ID.
            pmtStreamId = ((bytes[cursor+10] << 8) | bytes[cursor+11]) & 0x1FFF;

            CONFIG::LOGGING
            {
                logger.debug("Saw PMT ID of " + pmtStreamId);
            }

            return true;
        }

        private function parseProgramMapTable(bytes:ByteArray, cursor:uint):Boolean
        {
            var sectionLength:uint;
            var sectionLimit:uint;
            var programInfoLength:uint;
            var type:uint;
            var pid:uint;
            var esInfoLength:uint;
            var seenPIDsByClass:Array;
            var mediaClass:int;

            var hasAudio:Boolean = false, hasVideo:Boolean = false;

            // Set up types.
            types = [];
            seenPIDsByClass = [];
            seenPIDsByClass[MediaClass.VIDEO] = Infinity;
            seenPIDsByClass[MediaClass.AUDIO] = Infinity;

            // Process section length and limit.
            cursor++;

            sectionLength = ((bytes[cursor] & 0x0f) << 8) + bytes[cursor + 1];
            cursor += 2;

            if(sectionLength + cursor > bytes.length)
            {
                CONFIG::LOGGING
                {
                    logger.error("Not enough data to read. 1");
                }
                return false;
            }

            // Skip a few things we don't care about: program number, RSV, version, CNI, section, last_section, pcr_cid
            sectionLimit = cursor + sectionLength;
            cursor += 7;

            // And get the program info length.
            programInfoLength = ((bytes[cursor] & 0x0f) << 8) + bytes[cursor + 1];
            cursor += 2;

            // If not enough data to proceed, bail.
            if(programInfoLength + cursor > bytes.length)
            {
                CONFIG::LOGGING
                {
                    logger.error("Not enough data to read. 2");
                }
                return false;
            }

            cursor += programInfoLength;

            const CRC_SIZE:int = 4;
            while(cursor < sectionLimit - CRC_SIZE)
            {
                type = bytes[cursor++];
                pid = ((bytes[cursor] & 0x1f) << 8) + bytes[cursor + 1];
                cursor += 2;

                mediaClass = MediaClass.calculate(type);

                if(mediaClass == MediaClass.VIDEO)
                    hasVideo = true

                if(mediaClass == MediaClass.AUDIO)
                    hasAudio = true

                // For video & audio, select the lowest PID for each kind.
                if(mediaClass == MediaClass.OTHER
                 || pid < seenPIDsByClass[mediaClass])
                {
                    // Clear a higher PID if present.
                    if(mediaClass != MediaClass.OTHER
                     && seenPIDsByClass[mediaClass] < Infinity)
                        types[seenPIDsByClass[mediaClass]] = -1;

                    types[pid] = type;
                    seenPIDsByClass[mediaClass] = pid;
                }

                // Skip the esInfo data.
                esInfoLength = ((bytes[cursor] & 0x0f) << 8) + bytes[cursor + 1];
                cursor += 2;
                cursor += esInfoLength;
            }

            // Cook out header to transcoder.
            //transcoder.writeHeader(hasAudio, hasVideo);
            headerSent = true;

            return true;
        }

        public function append(packet:PESPacket):Boolean
        {
//            logger.debug("saw packet of " + packet.buffer.length);
            var b:ByteArray = packet.buffer;
            b.position = 0;

            if(b.length < 8)
            {
                CONFIG::LOGGING
                {
                    logger.error("Ignoring too short PES packet, length=" + b.length);
                }
                return true;
            }

            // Get the start code.
            var startCode:uint = b.readUnsignedInt();
            if((startCode & 0xFFFFFF00) != 0x00000100)
            {
                // It could be a program association table.
                if((startCode & 0xFFFFFF00) == 0x0000b000)
                {
                    parseProgramAssociationTable(b, 1);
                    return true;
                }

                // It could be the program map table.
                if((startCode & 0xFFFFFC00) == 0x0002b000)
                {
                    parseProgramMapTable(b, 1);
                    return true;
                }

                var tmp:ByteArray = new ByteArray();
                tmp.writeInt(startCode);
                CONFIG::LOGGING
                {
                    logger.error("ES prefix was wrong, expected 00:00:01:xx but got " + Hex.fromArray(tmp, true));
                }
                return true;
            }

            // Get the stream ID.
            var streamID:int = startCode & 0xFF;

            // Get the length.
            var packetLength:uint = b.readUnsignedShort();
            if(packetLength)
            {
                if(b.length < packetLength )
                {
                    CONFIG::LOGGING
                    {
                        logger.warn("WARNING: parsePESPacket - not enough bytes, expecting " + packetLength + ", but have " + b.length);
                    }
                    return false; // not enough bytes in packet
                }
            }

            if(b.length < 9)
            {
                CONFIG::LOGGING
                {
                    logger.warn("WARNING: parsePESPacket - too short to read header!");
                }
                return false;
            }

            // Read the rest of the header.
            var cursor:uint = 6;
            var dataAlignment:Boolean = (b[cursor] & 0x04) != 0;
            cursor++;

            var ptsDts:uint = (b[cursor] & 0xc0) >> 6;
            cursor++;

            var pesHeaderDataLength:uint = b[cursor];
            cursor++;

            //logger.debug(" PES align=" + dataAlignment + " ptsDts=" + ptsDts + " header=" + pesHeaderDataLength);

            var pts:Number = 0, dts:Number = 0;

            if(ptsDts & 0x02)
            {
                // has PTS at least
                if(cursor + 5 > b.length)
                    return true;

                pts  = b[cursor] & 0x0e;
                pts *= 128;
                pts += b[cursor + 1];
                pts *= 256;
                pts += b[cursor + 2] & 0xfe;
                pts *= 128;
                pts += b[cursor + 3];
                pts *= 256;
                pts += b[cursor + 4] & 0xfe;
                pts /= 2;

                if(ptsDts & 0x01)
                {
                    // DTS too!
                    if(cursor + 10 > b.length)
                        return true;

                    dts  = b[cursor + 5] & 0x0e;
                    dts *= 128;
                    dts += b[cursor + 6];
                    dts *= 256;
                    dts += b[cursor + 7] & 0xfe;
                    dts *= 128;
                    dts += b[cursor + 8];
                    dts *= 256;
                    dts += b[cursor + 9] & 0xfe;
                    dts /= 2;
                }
                else
                {
                    //logger.debug("Filling in DTS")
                    dts = pts;
                }
            }

            // Condition PTS and DTS.
            pts = handleMpegTimestampWrap(pts, lastPTS);
            lastPTS = pts;

            dts = handleMpegTimestampWrap(dts, lastDTS);
            lastDTS = dts;

            packet.pts = pts;
            packet.dts = dts;
            //logger.debug("   PTS=" + pts/90000 + " DTS=" + dts/90000);

            cursor += pesHeaderDataLength;

            if(cursor > b.length)
            {
                CONFIG::LOGGING
                {
                    logger.warn("WARNING: parsePESPacket - ran out of bytes");
                }
                return true;
            }

            if(types[packet.packetID] == undefined)
            {
                CONFIG::LOGGING
                {
                    logger.warn("WARNING: parsePESPacket - unknown type");
                }
                return true;
            }

            var pes:PESPacketStream;

            if(streams[packet.packetID] == undefined)
            {
                if(dts < 0.0)
                {
                    CONFIG::LOGGING
                    {
                        logger.warn("WARNING: parsePESPacket - invalid decode timestamp, skipping");
                    }
                    return true;
                }

                pes = new PESPacketStream();
                streams[packet.packetID] = pes;
            }
            else
            {
                pes = streams[packet.packetID];
            }

            if(headerSent == false)
            {
                CONFIG::LOGGING
                {
                    logger.warn("Skipping data that came before PMT");
                }
                return true;
            }

            // Note the type at this moment in time.
            packet.type = types[packet.packetID];
            packet.headerLength = cursor;

            // And process.
            if(MediaClass.calculate(types[packet.packetID]) == MediaClass.VIDEO)
            {
                var start:int = NALU.scan(b, cursor, true);
                if(start == -1 && lastVideoNALU)
                {
                    CONFIG::LOGGING
                    {
                        logger.debug("Stuff entire " + (b.length - cursor) + " bytes into previous NALU.");
                    }
                    lastVideoNALU.buffer.position = lastVideoNALU.buffer.length;
                    b.position = 0;
                    lastVideoNALU.buffer.writeBytes(b, cursor, b.length - cursor);
                    return true;

                }
                else if((start - cursor) > 0 && lastVideoNALU)
                {
                    // Shove into previous buffer.
                    CONFIG::LOGGING
                    {
                        logger.debug("Stuffing first " + (start - cursor) + " bytes into previous NALU.");
                    }
                    lastVideoNALU.buffer.position = lastVideoNALU.buffer.length;
                    b.position = 0;
                    lastVideoNALU.buffer.writeBytes(b, cursor, start - cursor);
                    cursor = start;
                }

                // If it's identical timestamps, accumulate it into the current unit and keep going.
                if(lastVideoNALU && pts == lastVideoNALU.pts && dts == lastVideoNALU.dts)
                {
                    CONFIG::LOGGING
                    {
                        logger.debug("Combining " + (start-cursor) + " bytes into previous NALU due to matching DTS/PTS.");
                    }
                    lastVideoNALU.buffer.position = lastVideoNALU.buffer.length;
                    b.position = 0;
                    lastVideoNALU.buffer.writeBytes(b, cursor, start - cursor);
                    cursor = start;
                }
                else
                {
                    // Submit previous data.
                    if(lastVideoNALU)
                    {
                        pendingBuffers.push(lastVideoNALU.clone());
                    }

                    // Update NALU state.
                    lastVideoNALU = new NALU();
                    lastVideoNALU.buffer = new ByteArray();
                    lastVideoNALU.pts = pts;
                    lastVideoNALU.dts = dts;
                    lastVideoNALU.type = packet.type;
                    lastVideoNALU.buffer.writeBytes(b, cursor);

                }
            }
            else if(types[packet.packetID] == 0x0F)
            {
                // It's an AAC stream.
                pendingBuffers.push(packet.clone());
            }
            else if(types[packet.packetID] == 0x03 || types[packet.packetID] == 0x04)
            {
                // It's an MP3 stream.
                pendingBuffers.push(packet.clone());
            }
            else
            {
                CONFIG::LOGGING
                {
                    logger.warn("Unknown packet ID type " + types[packet.packetID] + ", ignoring (A).");
                }
            }

            bufferPendingNalus();

            return true;
        }

        /**
         * To avoid transcoding all content on flush, we buffer it into FLV
         * tags as we go. However, they remain undelivered until we can gather
         * final SPS/PPS information. This method is responsible for
         * incrementally buffering in the FLV transcoder as we go.
         */
        public function bufferPendingNalus():void
        {
            // Iterate and buffer new NALUs.
            for(var i:int=pendingLastConvertedIndex; i<pendingBuffers.length; i++)
            {
                if(pendingBuffers[i] is NALU)
                {
                    transcoder.convert(pendingBuffers[i] as NALU);
                }
                else if(pendingBuffers[i] is PESPacket)
                {
                    var packet:PESPacket = pendingBuffers[i] as PESPacket;
                    if(packet.type == 0x0F)
                    {
                        // It's an AAC stream.
                        transcoder.convertAAC(packet);
                    }
                    else if(packet.type == 0x03 || packet.type == 0x04)
                    {
                        // It's an MP3 stream. Pass through directly.
                        transcoder.convertMP3(packet);
                    }
                    else
                    {
                        CONFIG::LOGGING
                        {
                            logger.warn("Unknown packet ID type " + packet.type + ", ignoring (B).");
                        }
                    }
                }
            }

            // Note the last item we converted so we can avoid duplicating work.
            pendingLastConvertedIndex = pendingBuffers.length;
        }

        public function processAllNalus():void
        {
            // Consume any unposted video NALUs.
            if(lastVideoNALU)
            {
                pendingBuffers.push(lastVideoNALU.clone());
                lastVideoNALU = null;
            }

            // First walk all the video NALUs and get the correct SPS/PPS
            if(pendingBuffers.length == 0)
                return;

            // Then emit SPS/PPS
            transcoder.emitSPSPPSUnbuffered();

            // Complete buffering and emit it all.
            bufferPendingNalus();
            transcoder.emitBufferedTags();

            // Don't forget to clear the pending list.
            pendingBuffers.length = 0;
            pendingLastConvertedIndex = 0;
        }
    }
}

package com.kaltura.hls.m2ts
{
    import flash.utils.ByteArray;
    import flash.net.ObjectEncoding;
    import flash.utils.ByteArray;
    import flash.utils.Endian;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import com.hurlant.util.Hex;

    CONFIG::LOGGING
    {
        import org.osmf.logging.Logger;
        import org.osmf.logging.Log;
    }

    /**
     * Process packetized elementary streams and extract NALUs and other data.
     */
    public class PESProcessor
    {
        CONFIG::LOGGING
        {
            private static const logger:Logger = Log.getLogger("com.kaltura.hls.m2ts.PESProcessor");
        }

        public var types:Object = {};
        public var streams:Object = {};

        public var lastVideoNALU:NALU = null;

        public var transcoder:FLVTranscoder = new FLVTranscoder();

        public var headerSent:Boolean = false;

        public var pmtStreamId:int = -1;

        protected var pendingBuffers:Vector.<Object> = new Vector.<Object>();
        protected var pendingLastConvertedIndex:int = 0;

        public var lastPTS:Number = 0, lastDTS:Number = 0;

        /**
         * Given a MPEG timestamp we've seen previously, determine if the new timestamp
         * has wrapped and correct it to follow the old timestamp.
         */
        static function handleMpegTimestampWrap(newTime:Number, oldTime:Number):Number
        {
            while (!isNaN(oldTime) && (Math.abs(newTime - oldTime) > 4294967296))
                newTime += (oldTime < newTime) ? -8589934592 : 8589934592;

            return newTime;
        }

        public function logStreams():void
        {
            CONFIG::LOGGING
            {
                logger.debug("----- PES state -----");
                for(var k:* in streams)
                {
                    logger.debug("   " + k + " has " + streams[k].buffer.length + " bytes, type=" + types[k]);
                }
            }
        }

        public function clear(clearAACConfig:Boolean = true):void
        {
            streams = {};
            lastVideoNALU = null;
            transcoder.clear(clearAACConfig);

            // Reset PTS/DTS reference.
            lastPTS = lastDTS = 0;
        }

        private function parseProgramAssociationTable(bytes:ByteArray, cursor:uint):Boolean
        {
            // Get the section length.
            var sectionLen:uint = ((bytes[cursor+2] & 0x03) << 8) | bytes[cursor+3];

            // Check the section length for a single PMT.
            CONFIG::LOGGING
            {
                if (sectionLen > 13)
                {
                    logger.debug("Saw multiple PMT entries in the PAT; blindly choosing first one.");
                }
            }

            // Grab the PMT ID.
            pmtStreamId = ((bytes[cursor+10] << 8) | bytes[cursor+11]) & 0x1FFF;

            CONFIG::LOGGING
            {
                logger.debug("Saw PMT ID of " + pmtStreamId);
            }

            return true;
        }

        private function parseProgramMapTable(bytes:ByteArray, cursor:uint):Boolean
        {
            var sectionLength:uint;
            var sectionLimit:uint;
            var programInfoLength:uint;
            var type:uint;
            var pid:uint;
            var esInfoLength:uint;
            var seenPIDsByClass:Array;
            var mediaClass:int;

            var hasAudio:Boolean = false, hasVideo:Boolean = false;

            // Set up types.
            types = [];
            seenPIDsByClass = [];
            seenPIDsByClass[MediaClass.VIDEO] = Infinity;
            seenPIDsByClass[MediaClass.AUDIO] = Infinity;

            // Process section length and limit.
            cursor++;

            sectionLength = ((bytes[cursor] & 0x0f) << 8) + bytes[cursor + 1];
            cursor += 2;

            if(sectionLength + cursor > bytes.length)
            {
                CONFIG::LOGGING
                {
                    logger.error("Not enough data to read. 1");
                }
                return false;
            }

            // Skip a few things we don't care about: program number, RSV, version, CNI, section, last_section, pcr_cid
            sectionLimit = cursor + sectionLength;
            cursor += 7;

            // And get the program info length.
            programInfoLength = ((bytes[cursor] & 0x0f) << 8) + bytes[cursor + 1];
            cursor += 2;

            // If not enough data to proceed, bail.
            if(programInfoLength + cursor > bytes.length)
            {
                CONFIG::LOGGING
                {
                    logger.error("Not enough data to read. 2");
                }
                return false;
            }

            cursor += programInfoLength;

            const CRC_SIZE:int = 4;
            while(cursor < sectionLimit - CRC_SIZE)
            {
                type = bytes[cursor++];
                pid = ((bytes[cursor] & 0x1f) << 8) + bytes[cursor + 1];
                cursor += 2;

                mediaClass = MediaClass.calculate(type);

                if(mediaClass == MediaClass.VIDEO)
                    hasVideo = true

                if(mediaClass == MediaClass.AUDIO)
                    hasAudio = true

                // For video & audio, select the lowest PID for each kind.
                if(mediaClass == MediaClass.OTHER
                 || pid < seenPIDsByClass[mediaClass])
                {
                    // Clear a higher PID if present.
                    if(mediaClass != MediaClass.OTHER
                     && seenPIDsByClass[mediaClass] < Infinity)
                        types[seenPIDsByClass[mediaClass]] = -1;

                    types[pid] = type;
                    seenPIDsByClass[mediaClass] = pid;
                }

                // Skip the esInfo data.
                esInfoLength = ((bytes[cursor] & 0x0f) << 8) + bytes[cursor + 1];
                cursor += 2;
                cursor += esInfoLength;
            }

            // Cook out header to transcoder.
            //transcoder.writeHeader(hasAudio, hasVideo);
            headerSent = true;

            return true;
        }

        public function append(packet:PESPacket):Boolean
        {
//            logger.debug("saw packet of " + packet.buffer.length);
            var b:ByteArray = packet.buffer;
            b.position = 0;

            if(b.length < 8)
            {
                CONFIG::LOGGING
                {
                    logger.error("Ignoring too short PES packet, length=" + b.length);
                }
                return true;
            }

            // Get the start code.
            var startCode:uint = b.readUnsignedInt();
            if((startCode & 0xFFFFFF00) != 0x00000100)
            {
                // It could be a program association table.
                if((startCode & 0xFFFFFF00) == 0x0000b000)
                {
                    parseProgramAssociationTable(b, 1);
                    return true;
                }

                // It could be the program map table.
                if((startCode & 0xFFFFFC00) == 0x0002b000)
                {
                    parseProgramMapTable(b, 1);
                    return true;
                }

                var tmp:ByteArray = new ByteArray();
                tmp.writeInt(startCode);
                CONFIG::LOGGING
                {
                    logger.error("ES prefix was wrong, expected 00:00:01:xx but got " + Hex.fromArray(tmp, true));
                }
                return true;
            }

            // Get the stream ID.
            var streamID:int = startCode & 0xFF;

            // Get the length.
            var packetLength:uint = b.readUnsignedShort();
            if(packetLength)
            {
                if(b.length < packetLength )
                {
                    CONFIG::LOGGING
                    {
                        logger.warn("WARNING: parsePESPacket - not enough bytes, expecting " + packetLength + ", but have " + b.length);
                    }
                    return false; // not enough bytes in packet
                }
            }

            if(b.length < 9)
            {
                CONFIG::LOGGING
                {
                    logger.warn("WARNING: parsePESPacket - too short to read header!");
                }
                return false;
            }

            // Read the rest of the header.
            var cursor:uint = 6;
            var dataAlignment:Boolean = (b[cursor] & 0x04) != 0;
            cursor++;

            var ptsDts:uint = (b[cursor] & 0xc0) >> 6;
            cursor++;

            var pesHeaderDataLength:uint = b[cursor];
            cursor++;

            //logger.debug(" PES align=" + dataAlignment + " ptsDts=" + ptsDts + " header=" + pesHeaderDataLength);

            var pts:Number = 0, dts:Number = 0;

            if(ptsDts & 0x02)
            {
                // has PTS at least
                if(cursor + 5 > b.length)
                    return true;

                pts  = b[cursor] & 0x0e;
                pts *= 128;
                pts += b[cursor + 1];
                pts *= 256;
                pts += b[cursor + 2] & 0xfe;
                pts *= 128;
                pts += b[cursor + 3];
                pts *= 256;
                pts += b[cursor + 4] & 0xfe;
                pts /= 2;

                if(ptsDts & 0x01)
                {
                    // DTS too!
                    if(cursor + 10 > b.length)
                        return true;

                    dts  = b[cursor + 5] & 0x0e;
                    dts *= 128;
                    dts += b[cursor + 6];
                    dts *= 256;
                    dts += b[cursor + 7] & 0xfe;
                    dts *= 128;
                    dts += b[cursor + 8];
                    dts *= 256;
                    dts += b[cursor + 9] & 0xfe;
                    dts /= 2;
                }
                else
                {
                    //logger.debug("Filling in DTS")
                    dts = pts;
                }
            }

            // Condition PTS and DTS.
            pts = handleMpegTimestampWrap(pts, lastPTS);
            lastPTS = pts;

            dts = handleMpegTimestampWrap(dts, lastDTS);
            lastDTS = dts;

            packet.pts = pts;
            packet.dts = dts;
            //logger.debug("   PTS=" + pts/90000 + " DTS=" + dts/90000);

            cursor += pesHeaderDataLength;

            if(cursor > b.length)
            {
                CONFIG::LOGGING
                {
                    logger.warn("WARNING: parsePESPacket - ran out of bytes");
                }
                return true;
            }

            if(types[packet.packetID] == undefined)
            {
                CONFIG::LOGGING
                {
                    logger.warn("WARNING: parsePESPacket - unknown type");
                }
                return true;
            }

            var pes:PESPacketStream;

            if(streams[packet.packetID] == undefined)
            {
                if(dts < 0.0)
                {
                    CONFIG::LOGGING
                    {
                        logger.warn("WARNING: parsePESPacket - invalid decode timestamp, skipping");
                    }
                    return true;
                }

                pes = new PESPacketStream();
                streams[packet.packetID] = pes;
            }
            else
            {
                pes = streams[packet.packetID];
            }

            if(headerSent == false)
            {
                CONFIG::LOGGING
                {
                    logger.warn("Skipping data that came before PMT");
                }
                return true;
            }

            // Note the type at this moment in time.
            packet.type = types[packet.packetID];
            packet.headerLength = cursor;

            // And process.
            if(MediaClass.calculate(types[packet.packetID]) == MediaClass.VIDEO)
            {
                var start:int = NALU.scan(b, cursor, true);
                if(start == -1 && lastVideoNALU)
                {
                    CONFIG::LOGGING
                    {
                        logger.debug("Stuff entire " + (b.length - cursor) + " bytes into previous NALU.");
                    }
                    lastVideoNALU.buffer.position = lastVideoNALU.buffer.length;
                    b.position = 0;
                    lastVideoNALU.buffer.writeBytes(b, cursor, b.length - cursor);
                    return true;

                }
                else if((start - cursor) > 0 && lastVideoNALU)
                {
                    // Shove into previous buffer.
                    CONFIG::LOGGING
                    {
                        logger.debug("Stuffing first " + (start - cursor) + " bytes into previous NALU.");
                    }
                    lastVideoNALU.buffer.position = lastVideoNALU.buffer.length;
                    b.position = 0;
                    lastVideoNALU.buffer.writeBytes(b, cursor, start - cursor);
                    cursor = start;
                }

                // If it's identical timestamps, accumulate it into the current unit and keep going.
                if(lastVideoNALU && pts == lastVideoNALU.pts && dts == lastVideoNALU.dts)
                {
                    CONFIG::LOGGING
                    {
                        logger.debug("Combining " + (start-cursor) + " bytes into previous NALU due to matching DTS/PTS.");
                    }
                    lastVideoNALU.buffer.position = lastVideoNALU.buffer.length;
                    b.position = 0;
                    lastVideoNALU.buffer.writeBytes(b, cursor, start - cursor);
                    cursor = start;
                }
                else
                {
                    // Submit previous data.
                    if(lastVideoNALU)
                    {
                        pendingBuffers.push(lastVideoNALU.clone());
                    }

                    // Update NALU state.
                    lastVideoNALU = new NALU();
                    lastVideoNALU.buffer = new ByteArray();
                    lastVideoNALU.pts = pts;
                    lastVideoNALU.dts = dts;
                    lastVideoNALU.type = packet.type;
                    lastVideoNALU.buffer.writeBytes(b, cursor);

                }
            }
            else if(types[packet.packetID] == 0x0F)
            {
                // It's an AAC stream.
                pendingBuffers.push(packet.clone());
            }
            else if(types[packet.packetID] == 0x03 || types[packet.packetID] == 0x04)
            {
                // It's an MP3 stream.
                pendingBuffers.push(packet.clone());
            }
            else
            {
                CONFIG::LOGGING
                {
                    logger.warn("Unknown packet ID type " + types[packet.packetID] + ", ignoring (A).");
                }
            }

            bufferPendingNalus();

            return true;
        }

        /**
         * To avoid transcoding all content on flush, we buffer it into FLV
         * tags as we go. However, they remain undelivered until we can gather
         * final SPS/PPS information. This method is responsible for
         * incrementally buffering in the FLV transcoder as we go.
         */
        public function bufferPendingNalus():void
        {
            // Iterate and buffer new NALUs.
            for(var i:int=pendingLastConvertedIndex; i<pendingBuffers.length; i++)
            {
                if(pendingBuffers[i] is NALU)
                {
                    transcoder.convert(pendingBuffers[i] as NALU);
                }
                else if(pendingBuffers[i] is PESPacket)
                {
                    var packet:PESPacket = pendingBuffers[i] as PESPacket;
                    if(packet.type == 0x0F)
                    {
                        // It's an AAC stream.
                        transcoder.convertAAC(packet);
                    }
                    else if(packet.type == 0x03 || packet.type == 0x04)
                    {
                        // It's an MP3 stream. Pass through directly.
                        transcoder.convertMP3(packet);
                    }
                    else
                    {
                        CONFIG::LOGGING
                        {
                            logger.warn("Unknown packet ID type " + packet.type + ", ignoring (B).");
                        }
                    }
                }
            }

            // Note the last item we converted so we can avoid duplicating work.
            pendingLastConvertedIndex = pendingBuffers.length;
        }

        public function processAllNalus():void
        {
            // Consume any unposted video NALUs.
            if(lastVideoNALU)
            {
                pendingBuffers.push(lastVideoNALU.clone());
                lastVideoNALU = null;
            }

            // First walk all the video NALUs and get the correct SPS/PPS
            if(pendingBuffers.length == 0)
                return;

            // Then emit SPS/PPS
            transcoder.emitSPSPPSUnbuffered();

            // Complete buffering and emit it all.
            bufferPendingNalus();
            transcoder.emitBufferedTags();

            // Don't forget to clear the pending list.
            pendingBuffers.length = 0;
            pendingLastConvertedIndex = 0;
        }
    }
}

Fix for scrub/reset cases.

Fix for scrub/reset cases.


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

	/**
	 * The BillboardsGeometry stores a list of 2D billboards meant to be used
	 * in particle effects or 2D sprites rendering.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class BillboardsGeometry extends Geometry
	{
		public static const PARTICLE_FORMAT	: VertexFormat	= new VertexFormat(
			VertexComponent.XY,
			VertexComponent.ID
		);

		public function BillboardsGeometry(numQuads : uint)
		{
			super();

			initialize(numQuads);
		}

		private function initialize(numQuads : uint) : void
		{
			var vertices	: Vector.<Number>	= new <Number>[];
			var indices		: Vector.<uint>		= new <uint>[];

			for (var particleId : int = 0; particleId < numQuads; ++particleId)
			{
				vertices.push(
					-0.5, 0.5, particleId,
					0.5, 0.5, particleId,
					0.5, -0.5, particleId,
					-0.5, -0.5, particleId
				);

				indices.push(
					particleId * 4, particleId * 4 + 2, particleId * 4 + 1,
					particleId * 4, particleId * 4 + 3, particleId * 4 + 2
				);
			}

			setVertexStream(
				new VertexStream(
					StreamUsage.STATIC,
					PARTICLE_FORMAT,
					vertices
				),
				0
			);

			indexStream = new IndexStream(StreamUsage.STATIC, indices);
		}
	}
}

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

	/**
	 * The BillboardsGeometry stores a list of 2D billboards meant to be used
	 * in particle effects or 2D sprites rendering.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class BillboardsGeometry extends Geometry
	{
		public static const BILLBOARD_FORMAT	: VertexFormat	= new VertexFormat(
			VertexComponent.XY,
			VertexComponent.ID
		);

		public function BillboardsGeometry(numQuads : uint)
		{
			super();

			initialize(numQuads);
		}

		private function initialize(numBillboards : uint) : void
		{
			var vertices	: Vector.<Number>	= new <Number>[];
			var indices		: Vector.<uint>		= new <uint>[];

			for (var billboardId : int = 0; billboardId < numBillboards; ++billboardId)
			{
				vertices.push(
					-0.5, 0.5, billboardId,
					0.5, 0.5, billboardId,
					0.5, -0.5, billboardId,
					-0.5, -0.5, billboardId
				);

				indices.push(
					billboardId * 4, billboardId * 4 + 2, billboardId * 4 + 1,
					billboardId * 4, billboardId * 4 + 3, billboardId * 4 + 2
				);
			}

			setVertexStream(new VertexStream(StreamUsage.STATIC, BILLBOARD_FORMAT, vertices), 0);
			indexStream = new IndexStream(StreamUsage.STATIC, indices);
		}
	}
}

fix BillboardsGeometry variables/constants naming


package classes
{
	/**
	 AGORA - an interactive and web-based argument mapping tool that stimulates reasoning,
	 reflection, critique, deliberation, and creativity in individual argument construction
	 and in collaborative or adversarial settings.
	 Copyright (C) 2011 Georgia Institute of Technology

	 This program is free software: you can redistribute it and/or modify
	 it under the terms of the GNU Affero General Public License as
	 published by the Free Software Foundation, either version 3 of the
	 License, or (at your option) any later version.

	 This program is distributed in the hope that it will be useful,
	 but WITHOUT ANY WARRANTY; without even the implied warranty of
	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	 GNU Affero General Public License for more details.

	 You should have received a copy of the GNU Affero General Public License
	 along with this program.  If not, see <http://www.gnu.org/licenses/>.

	 */
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;

	import mx.controls.Alert;

	import org.osmf.layout.AbsoluteLayoutFacet;

	public class Language
	{
		//I will set the language to either GERMAN, RUSSIAN or ENGLISH
		//based on this, you could read the values into the variables
		//by default, the language is EN-US

		public static const GERMAN:String = "GER";
		public static const ENGLISH:String = "EN-US";
		public static const RUSSIAN:String = "RUS";
		public static var language:String = GERMAN;

		public static var xml:XML;
		private static var ready:Boolean=false;

		public static function init():void
		{
			[Embed(source="../../../translation.xml", mimeType="application/octet-stream")]
			const MyData:Class;
			var byteArray:ByteArray = new MyData() as ByteArray;
			var x:XML = new XML(byteArray.readUTFBytes(byteArray.length));
			xml = x;
			ready=true;
		}

		/**The key function. Use this to look up a label from the translation document according to the set language.*/

		public static function lookup(label:String):String{
			if(!ready){
				init();
			}
			trace("Now looking up:" + label);
			var lbl:XMLList = xml.descendants(label);
			var lang:XMLList = lbl.descendants(language);
			var output:String = lang.attribute("text");
			if(!output){
				output = "error | ошибка | Fehler --- There was a problem getting the text for this item. The label was: " + label;
			}
			trace("Output is: " + output);
			return output;
		}
	}
}

package classes
{
	/**
	 AGORA - an interactive and web-based argument mapping tool that stimulates reasoning,
	 reflection, critique, deliberation, and creativity in individual argument construction
	 and in collaborative or adversarial settings.
	 Copyright (C) 2011 Georgia Institute of Technology

	 This program is free software: you can redistribute it and/or modify
	 it under the terms of the GNU Affero General Public License as
	 published by the Free Software Foundation, either version 3 of the
	 License, or (at your option) any later version.

	 This program is distributed in the hope that it will be useful,
	 but WITHOUT ANY WARRANTY; without even the implied warranty of
	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	 GNU Affero General Public License for more details.

	 You should have received a copy of the GNU Affero General Public License
	 along with this program.  If not, see <http://www.gnu.org/licenses/>.

	 */
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;

	import mx.controls.Alert;

	import org.osmf.layout.AbsoluteLayoutFacet;

	public class Language
	{
		//I will set the language to either GERMAN, RUSSIAN or ENGLISH
		//based on this, you could read the values into the variables
		//by default, the language is EN-US

		public static const GERMAN:String = "GER";
		public static const ENGLISH:String = "EN-US";
		public static const RUSSIAN:String = "RUS";
		public static var language:String = ENGLISH;

		public static var xml:XML;
		private static var ready:Boolean=false;

		public static function init():void
		{
			[Embed(source="../../../translation.xml", mimeType="application/octet-stream")]
			const MyData:Class;
			var byteArray:ByteArray = new MyData() as ByteArray;
			var x:XML = new XML(byteArray.readUTFBytes(byteArray.length));
			xml = x;
			ready=true;
		}

		/**The key function. Use this to look up a label from the translation document according to the set language.*/

		public static function lookup(label:String):String{
			if(!ready){
				init();
			}
			trace("Now looking up:" + label);
			var lbl:XMLList = xml.descendants(label);
			var lang:XMLList = lbl.descendants(language);
			var output:String = lang.attribute("text");
			if(!output){
				output = "error | ошибка | Fehler --- There was a problem getting the text for this item. The label was: " + label;
			}
			trace("Output is: " + output);
			return output;
		}
	}
}

Set language back to english

Set language back to english


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

	import org.apache.flex.core.ValuesManager;
    import org.apache.flex.events.Event;
    import org.apache.flex.utils.CSSUtils;

    /**
     *  The CSSTextField class implements CSS text styles in a TextField.
     *  Not every CSS text style is currently supported.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class CSSTextField extends TextField
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function CSSTextField()
		{
			super();
		}

        /**
         *  @private
         *  The styleParent property is set if the CSSTextField
         *  is used in a SimpleButton-based instance because
         *  the parent property is null, defeating CSS lookup.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public var styleParent:Object;

        /**
         *  @private
         *  The parentDrawsBackground property is set if the CSSTextField
         *  shouldn't draw a background
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public var parentDrawsBackground:Boolean;

        /**
         *  @private
         *  The parentHandlesPadding property is set if the CSSTextField
         *  shouldn't worry about padding
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public var parentHandlesPadding:Boolean;

        /**
         *  @private
         */
		override public function set text(value:String):void
		{
			var sp:Object = parent;
			if (styleParent)
				sp = styleParent;
			sp.addEventListener("classNameChanged", updateStyles);

			var tf: TextFormat = new TextFormat();
			tf.font = ValuesManager.valuesImpl.getValue(sp, "fontFamily") as String;
			tf.size = ValuesManager.valuesImpl.getValue(sp, "fontSize");
			tf.bold = ValuesManager.valuesImpl.getValue(sp, "fontWeight") == "bold";
			tf.color = CSSUtils.toColor(ValuesManager.valuesImpl.getValue(sp, "color"));
            if (!parentHandlesPadding)
            {
        		var padding:Object = ValuesManager.valuesImpl.getValue(sp, "padding");
        		var paddingLeft:Object = ValuesManager.valuesImpl.getValue(sp,"padding-left");
        		var paddingRight:Object = ValuesManager.valuesImpl.getValue(sp,"padding-right");
        		tf.leftMargin = CSSUtils.getLeftValue(paddingLeft, padding, width);
        		tf.rightMargin = CSSUtils.getRightValue(paddingRight, padding, width);
            }
            var align:Object = ValuesManager.valuesImpl.getValue(sp, "text-align");
            if (align == "center")
			{
				autoSize = TextFieldAutoSize.NONE;
                tf.align = "center";
			}
            else if (align == "right")
			{
                tf.align = "right";
				autoSize = TextFieldAutoSize.NONE;
			}
            if (!parentDrawsBackground)
            {
                var backgroundColor:Object = ValuesManager.valuesImpl.getValue(sp, "background-color");
                if (backgroundColor != null)
                {
                    this.background = true;
                    this.backgroundColor = CSSUtils.toColor(backgroundColor);
                }
            }
			defaultTextFormat = tf;
			super.text = value;
		}

        private function updateStyles(event:Event):void
        {
            // force styles to be re-calculated
            this.text = text;
        }
	}
}

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

    import org.apache.flex.core.ValuesManager;
    import org.apache.flex.events.Event;
    import org.apache.flex.utils.CSSUtils;

    /**
     *  The CSSTextField class implements CSS text styles in a TextField.
     *  Not every CSS text style is currently supported.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class CSSTextField extends TextField
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function CSSTextField()
		{
			super();
		}

        /**
         *  @private
         *  The styleParent property is set if the CSSTextField
         *  is used in a SimpleButton-based instance because
         *  the parent property is null, defeating CSS lookup.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public var styleParent:Object;

        /**
         *  @private
         *  The CSS pseudo-state for lookups.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public var styleState:String;

        /**
         *  @private
         *  The parentDrawsBackground property is set if the CSSTextField
         *  shouldn't draw a background
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public var parentDrawsBackground:Boolean;

        /**
         *  @private
         *  The parentHandlesPadding property is set if the CSSTextField
         *  shouldn't worry about padding
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public var parentHandlesPadding:Boolean;

        /**
         *  @private
         */
		override public function set text(value:String):void
		{
			var sp:Object = parent;
			if (styleParent)
				sp = styleParent;
			sp.addEventListener("classNameChanged", updateStyles);

			var tf: TextFormat = new TextFormat();
			tf.font = ValuesManager.valuesImpl.getValue(sp, "fontFamily", styleState) as String;
			tf.size = ValuesManager.valuesImpl.getValue(sp, "fontSize", styleState);
			tf.bold = ValuesManager.valuesImpl.getValue(sp, "fontWeight", styleState) == "bold";
			tf.color = CSSUtils.toColor(ValuesManager.valuesImpl.getValue(sp, "color", styleState));
            if (!parentHandlesPadding)
            {
        		var padding:Object = ValuesManager.valuesImpl.getValue(sp, "padding", styleState);
        		var paddingLeft:Object = ValuesManager.valuesImpl.getValue(sp,"padding-left", styleState);
        		var paddingRight:Object = ValuesManager.valuesImpl.getValue(sp,"padding-right", styleState);
        		tf.leftMargin = CSSUtils.getLeftValue(paddingLeft, padding, width);
        		tf.rightMargin = CSSUtils.getRightValue(paddingRight, padding, width);
            }
            var align:Object = ValuesManager.valuesImpl.getValue(sp, "text-align", styleState);
            if (align == "center")
			{
				autoSize = TextFieldAutoSize.NONE;
                tf.align = "center";
			}
            else if (align == "right")
			{
                tf.align = "right";
				autoSize = TextFieldAutoSize.NONE;
			}
            if (!parentDrawsBackground)
            {
                var backgroundColor:Object = ValuesManager.valuesImpl.getValue(sp, "background-color", styleState);
                if (backgroundColor != null)
                {
                    this.background = true;
                    this.backgroundColor = CSSUtils.toColor(backgroundColor);
                }
            }
			defaultTextFormat = tf;
			super.text = value;
		}

        private function updateStyles(event:Event):void
        {
            // force styles to be re-calculated
            this.text = text;
        }

	}
}

allow pseudo-state specific styles

allow pseudo-state specific styles