package com.mangui.HLS.muxing {

    import com.mangui.HLS.utils.*;
    import flash.utils.ByteArray;

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

        /** H264 NAL unit names. **/
        public static const NAMES:Array = [
            'Unspecified',
            'NDR',
            'Partition A',
            'Partition B',
            'Partition C',
            'IDR',
            'SEI',
            'SPS',
            'PPS',
            'AUD',
            'End of Sequence',
            'End of Stream',
            'Filler Data'
        ];
        /** H264 profiles. **/
        public static const PROFILES:Object = {
            '66': 'H264 Baseline',
            '77': 'H264 Main',
            '100': 'H264 High'
        };

        /** Get Avcc header from AVC stream. **/
        public static function getAVCC(nalu:ByteArray,position:Number=0):ByteArray {
            // Find SPS and PPS units in AVC stream.
            var units:Array = AVC.getNALU(nalu,position,false);
            var sps:Number = -1;
            var pps:Number = -1;
            for(var i:Number = 0; i< units.length; i++) {
                if(units[i].type == 7 && sps == -1) {
                    sps = i;
                } else if (units[i].type == 8 && pps == -1) {
                    pps = i;
                }
            }
            // Throw errors if units not found.
            if(sps == -1) {
                throw new Error("No SPS NAL unit found in this stream.");
            } else if (pps == -1) {
                throw new Error("No PPS NAL unit found in this stream.");
            }
            // Write startbyte, profile, compatibility and level.
            var avcc:ByteArray = new ByteArray();
            avcc.writeByte(0x01);
            avcc.writeBytes(nalu,units[sps].start+1, 3);
            // 111111 + NALU bytesize length (4?)
            avcc.writeByte(0xFF);
            // Number of SPS, Bytesize and data.
            avcc.writeByte(0xE1);
            avcc.writeShort(units[sps].length);
            avcc.writeBytes(nalu,units[sps].start,units[sps].length);
            // Number of PPS, Bytesize and data.
            avcc.writeByte(0x01);
            avcc.writeShort(units[pps].length);
            avcc.writeBytes(nalu,units[pps].start,units[pps].length);
            // Grab profile/level
            avcc.position = 1;
            var prf:Number = avcc.readByte();
            avcc.position = 3;
            var lvl:Number = avcc.readByte();
            avcc.position = 0;
            // Log.txt("AVC: "+PROFILES[prf]+' level '+lvl);
            return avcc;
        };

        /** Return an array with NAL delimiter indexes. **/
        public static function getNALU(nalu:ByteArray,position:Number=0,log:Boolean=true):Array {
            var units:Array = [];
            var unit_start:Number;
            var unit_type:Number;
            var unit_header:Number;
            // Loop through data to find NAL startcodes.
            var window:uint = 0;
            nalu.position = position;
            while(nalu.bytesAvailable > 4) {
                window = nalu.readUnsignedInt();
                // Match four-byte startcodes
                if((window & 0xFFFFFFFF) == 0x01) {
                    if(unit_start) {
                        units.push({
                            length: nalu.position - 4 - unit_start,
                            start: unit_start,
                            type: unit_type
                        });
                    }
                    unit_header = 4;
                    unit_start = nalu.position;
                    unit_type = nalu.readByte() & 0x1F;
                    if(unit_type == 1 || unit_type == 5) { break; }
                // Match three-byte startcodes
                } else if((window & 0xFFFFFF00) == 0x100) {
                    if(unit_start) {
                        units.push({
                            header: unit_header,
                            length: nalu.position - 4 - unit_start,
                            start: unit_start,
                            type: unit_type
                        });
                    }
                    nalu.position--;
                    unit_header = 3;
                    unit_start = nalu.position;
                    unit_type = nalu.readByte() & 0x1F;
                    if(unit_type == 1 || unit_type == 5) { break; }
                } else {
                    nalu.position -= 3;
                }
            }
            // Append the last NAL to the array.
            if(unit_start) {
                units.push({
                    header: unit_header,
                    length: nalu.length - unit_start,
                    start: unit_start,
                    type: unit_type
                });
            }
            // Reset position and return results.
            if(log) {
                if(units.length) {
                    var txt:String = "AVC: ";
                    for(var i:Number=0; i<units.length; i++) {
                        txt += NAMES[units[i].type] + ", ";
                    }
                    // Log.txt(txt.substr(0,txt.length-2) + " slices");
                } else {
                   // Log.txt('AVC: no NALU slices found');
                }
            }
            nalu.position = position;
            return units;
        };

    }

}

package com.mangui.HLS.muxing {

    import com.mangui.HLS.utils.*;
    import flash.utils.ByteArray;

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

        /** H264 NAL unit names. **/
        public static const NAMES:Array = [
            'Unspecified',
            'NDR',
            'Partition A',
            'Partition B',
            'Partition C',
            'IDR',
            'SEI',
            'SPS',
            'PPS',
            'AUD',
            'End of Sequence',
            'End of Stream',
            'Filler Data'
        ];
        /** H264 profiles. **/
        public static const PROFILES:Object = {
            '66': 'H264 Baseline',
            '77': 'H264 Main',
            '100': 'H264 High'
        };

        /** Get Avcc header from AVC stream. **/
        public static function getAVCC(nalu:ByteArray,position:Number=0):ByteArray {
            // Find SPS and PPS units in AVC stream.
            var units:Array = AVC.getNALU(nalu,position,false);
            var sps:Number = -1;
            var pps:Number = -1;
            for(var i:Number = 0; i< units.length; i++) {
                if(units[i].type == 7 && sps == -1) {
                    sps = i;
                } else if (units[i].type == 8 && pps == -1) {
                    pps = i;
                }
            }
            // Throw errors if units not found.
            if(sps == -1) {
                throw new Error("No SPS NAL unit found in this stream.");
            } else if (pps == -1) {
                throw new Error("No PPS NAL unit found in this stream.");
            }
            // Write startbyte
            var avcc:ByteArray = new ByteArray();
            avcc.writeByte(0x01);
            // Write profile, compatibility and level.
            avcc.writeBytes(nalu,units[sps].start+1, 3);
            // reserved (6 bits), NALU length size - 1 (2 bits)
            avcc.writeByte(0xFC | 3);
            // reserved (3 bits), num of SPS (5 bits)
            avcc.writeByte(0xE0 | 1);
            // 2 bytes for length of SPS
            avcc.writeShort(units[sps].length);
            // data of SPS
            avcc.writeBytes(nalu,units[sps].start,units[sps].length);
            // Number of PPS
            avcc.writeByte(0x01);
            // 2 bytes for length of PPS
            avcc.writeShort(units[pps].length);
            // data of PPS
            avcc.writeBytes(nalu,units[pps].start,units[pps].length);
            // Grab profile/level
            avcc.position = 1;
            var prf:Number = avcc.readByte();
            avcc.position = 3;
            var lvl:Number = avcc.readByte();
            avcc.position = 0;
            // Log.txt("AVC: "+PROFILES[prf]+' level '+lvl);
            return avcc;
        };

        /** Return an array with NAL delimiter indexes. **/
        public static function getNALU(nalu:ByteArray,position:Number=0,log:Boolean=true):Array {
            var units:Array = [];
            var unit_start:Number;
            var unit_type:Number;
            var unit_header:Number;
            // Loop through data to find NAL startcodes.
            var window:uint = 0;
            nalu.position = position;
            while(nalu.bytesAvailable > 4) {
                window = nalu.readUnsignedInt();
                // Match four-byte startcodes
                if((window & 0xFFFFFFFF) == 0x01) {
                    if(unit_start) {
                        units.push({
                            length: nalu.position - 4 - unit_start,
                            start: unit_start,
                            type: unit_type
                        });
                    }
                    unit_header = 4;
                    unit_start = nalu.position;
                    unit_type = nalu.readByte() & 0x1F;
                    // NDR or IDR NAL unit
                    if(unit_type == 1 || unit_type == 5) { break; }
                // Match three-byte startcodes
                } else if((window & 0xFFFFFF00) == 0x100) {
                    if(unit_start) {
                        units.push({
                            header: unit_header,
                            length: nalu.position - 4 - unit_start,
                            start: unit_start,
                            type: unit_type
                        });
                    }
                    nalu.position--;
                    unit_header = 3;
                    unit_start = nalu.position;
                    unit_type = nalu.readByte() & 0x1F;
                    // NDR or IDR NAL unit
                    if(unit_type == 1 || unit_type == 5) { break; }
                } else {
                    nalu.position -= 3;
                }
            }
            // Append the last NAL to the array.
            if(unit_start) {
                units.push({
                    header: unit_header,
                    length: nalu.length - unit_start,
                    start: unit_start,
                    type: unit_type
                });
            }
            // Reset position and return results.
            if(log) {
                if(units.length) {
                    var txt:String = "AVC: ";
                    for(var i:Number=0; i<units.length; i++) {
                        txt += NAMES[units[i].type] + ", ";
                    }
                    // Log.txt(txt.substr(0,txt.length-2) + " slices");
                } else {
                   // Log.txt('AVC: no NALU slices found');
                }
            }
            nalu.position = position;
            return units;
        };

    }

}

add comments for NAL unit parsing

add comments for NAL unit parsing


package com.axis.rtspclient {

  import flash.events.EventDispatcher;
  import flash.events.Event;
  import flash.utils.ByteArray;
  import flash.net.Socket;
  import flash.net.NetConnection;
  import flash.net.NetStream;
  import flash.media.Video;
  import mx.utils.StringUtil;

  import com.axis.rtspclient.FLVMux;
  import com.axis.rtspclient.RTP;
  import com.axis.rtspclient.SDP;
  import com.axis.http.url;
  import com.axis.http.request;
  import com.axis.http.auth;
  import com.axis.IClient;
  import com.axis.ClientEvent;

  public class RTSPClient extends EventDispatcher implements IClient {
    [Embed(source = "../../../../VERSION", mimeType = "application/octet-stream")] private var Version:Class;
    private var userAgent:String;

    private static var STATE_INITIAL:uint  = 1 << 0;
    private static var STATE_OPTIONS:uint  = 1 << 1;
    private static var STATE_DESCRIBE:uint = 1 << 2;
    private static var STATE_SETUP:uint    = 1 << 3;
    private static var STATE_PLAY:uint     = 1 << 4;
    private static var STATE_PLAYING:uint  = 1 << 5;
    private static var STATE_PAUSE:uint    = 1 << 6;
    private static var STATE_PAUSED:uint   = 1 << 7;
    private static var STATE_TEARDOWN:uint = 1 << 8;

    private var state:int = STATE_INITIAL;
    private var handle:IRTSPHandle;
    private var ns:NetStream;
    private var video:Video;

    private var sdp:SDP = new SDP();
    private var flvmux:FLVMux;

    private var urlParsed:Object;
    private var cSeq:uint = 1;
    private var session:String;
    private var contentBase:String;
    private var interleaveChannelIndex:uint = 0;

    private var methods:Array = [];
    private var data:ByteArray = new ByteArray();
    private var rtpLength:int = -1;
    private var rtpChannel:int = -1;
    private var tracks:Array;

    private var authState:String = "none";
    private var authOpts:Object = {};
    private var digestNC:uint = 1;

    public function RTSPClient(video:Video, urlParsed:Object, handle:IRTSPHandle) {
      this.userAgent = "Slush " + StringUtil.trim(new Version().toString());
      this.state = STATE_INITIAL;
      this.handle = handle;
      this.ns = ns;
      this.video = video;
      this.urlParsed = urlParsed;

      handle.addEventListener('data', this.onData);
    }

    public function start():Boolean {
      var self:RTSPClient = this;
      handle.addEventListener('connected', function():void {
        if (state !== STATE_INITIAL) {
          trace('Cannot start unless in initial state.');
          return;
        }

        /* If the handle closes, take care of it */
        handle.addEventListener('closed', self.onClose);

        if (0 === self.methods.length) {
          /* We don't know the options yet. Start with that. */
          sendOptionsReq();
        } else {
          /* Already queried the options (and perhaps got unauthorized on describe) */
          sendDescribeReq();
        }
      });

      var nc:NetConnection = new NetConnection();
      nc.connect(null);
      this.ns = new NetStream(nc);
      dispatchEvent(new ClientEvent(ClientEvent.NETSTREAM_CREATED, { ns : this.ns }));
      video.attachNetStream(this.ns);

      handle.connect();
      return true;
    }

    public function pause():Boolean
    {
      if (state !== STATE_PLAYING) {
        trace('Unable to pause a stream if not playing.');
        return false;
      }

      try {
        sendPauseReq();
      } catch (err:Error) {
        trace("Unable to pause: " + err.message);
        return false;
      }

      return true;
    }

    public function resume():Boolean
    {
      if (state !== STATE_PAUSED) {
        trace('Unable to resume a stream if not paused.');
        return false;
      }

      sendPlayReq();
      return true;
    }

    public function stop():Boolean
    {
      if (state < STATE_PLAY) {
        trace('Unable to stop if we never reached play.');
        return false;
      }
      sendTeardownReq();
      return true;
    }

    private function onClose(event:Event):void
    {
      if (state === STATE_TEARDOWN) {
        dispatchEvent(new ClientEvent(ClientEvent.STOPPED));
      } else {
        trace('RTSPClient: Handle unexpectedly closed.');
      }
    }

    private function onData(event:Event):void
    {
      /* read one byte to determine destination */
      if (0 < data.bytesAvailable) {
        /* Determining byte have already been read. This is a continuation */
      } else {
        /* Read the determining byte */
        handle.readBytes(data, 0, 1);
      }

      switch(data[0]) {
        case 0x52:
          /* ascii 'R', start of RTSP */
          onRTSPCommand();
          break;

        case 0x24:
          /* ascii '$', start of interleaved packet */
          onInterleavedData();
          break;

        default:
          trace('Unknown determining byte:', data[0]);
          break;
      }
    }

    private function requestReset():void
    {
      var copy:ByteArray = new ByteArray();
      data.readBytes(copy);
      data.clear();
      copy.readBytes(data);

      rtpLength  = -1;
      rtpChannel = -1;
    }

    private function readRequest(oBody:ByteArray):*
    {
      var parsed:* = request.readHeaders(handle, data);
      if (false === parsed) {
        return false;
      }

      if (401 === parsed.code) {
        /* Unauthorized, change authState and (possibly) try again */
        authOpts = parsed.headers['www-authenticate'];
        var newAuthState:String = auth.nextMethod(authState, authOpts);
        if (authState === newAuthState) {
          trace('GET: Exhausted all authentication methods.');
          trace('GET: Unable to authorize to ' + urlParsed.host);
          return false;
        }

        trace('RTSPClient: switching http-authorization from ' + authState + ' to ' + newAuthState);
        authState = newAuthState;
        state = STATE_INITIAL;
        data = new ByteArray();
        handle.reconnect();
        return false;
      }

      if (data.bytesAvailable < parsed.headers['content-length']) {
        return false;
      }

      /* RTSP commands contain no heavy body, so it's safe to read everything */
      data.readBytes(oBody, 0, parsed.headers['content-length']);
      requestReset();
      return parsed;
    }

    private function onRTSPCommand():void {
      var parsed:*, body:ByteArray = new ByteArray();
      if (false === (parsed = readRequest(body))) {
        return;
      }

      if (200 !== parsed.code) {
        trace('RTSPClient: Invalid RTSP response - ', parsed.code, parsed.message);
        return;
      }

      switch (state) {
      case STATE_INITIAL:
        trace("RTSPClient: STATE_INITIAL");

      case STATE_OPTIONS:
        trace("RTSPClient: STATE_DESCRIBE");
        this.methods = parsed.headers.public.split(/[ ]*,[ ]*/);
        sendDescribeReq();

        break;
      case STATE_DESCRIBE:
        trace("RTSPClient: STATE_DESCRIBE");

        if (!sdp.parse(body)) {
          trace("RTSPClient:Failed to parse SDP file");
          return;
        }

        if (!parsed.headers['content-base']) {
          trace('RTSPClient: no content-base in describe reply');
          return;
        }

        contentBase = parsed.headers['content-base'];
        tracks = sdp.getMediaBlockList();
        trace('SDP contained ' + tracks.length + ' track(s). Calling SETUP for each.');

        if (0 === tracks.length) {
          trace('No tracks in SDP file.');
          return;
        }

        /* Fall through, it's time for setup */
      case STATE_SETUP:
        trace("RTSPClient: STATE_SETUP");

        if (parsed.headers['session']) {
          session = parsed.headers['session'];
        }

        if (0 !== tracks.length) {
          /* More tracks we must setup before playing */
          var block:Object = tracks.shift();
          sendSetupReq(block);
          return;
        }

        /* All tracks setup and ready to go! */
        sendPlayReq();
        break;

      case STATE_PLAY:
        trace("RTSPClient: STATE_PLAY");
        state = STATE_PLAYING;

        if (this.flvmux) {
          /* If the flvmux have been initialized don't do it again.
             this is probably a resume after pause */
          break;
        }

        this.flvmux = new FLVMux(this.ns, this.sdp);
        var analu:ANALU = new ANALU();
        var aaac:AAAC = new AAAC(sdp);

        this.addEventListener("VIDEO_PACKET", analu.onRTPPacket);
        this.addEventListener("AUDIO_PACKET", aaac.onRTPPacket);
        analu.addEventListener(NALU.NEW_NALU, flvmux.onNALU);
        aaac.addEventListener(AACFrame.NEW_FRAME, flvmux.onAACFrame);
        break;

      case STATE_PLAYING:
        trace("RTSPClient: STATE_PLAYING");
        break;

      case STATE_PAUSE:
        trace("RTSPClient: STATE_PAUSE");
        state = STATE_PAUSED;
        break;

      case STATE_TEARDOWN:
        trace('RTSPClient: STATE_TEARDOWN');
        handle.disconnect();
        break;
      }

      if (0 < data.bytesAvailable) {
        onData(null);
      }
    }

    private function onInterleavedData():void
    {
      handle.readBytes(data, data.length);

      if (-1 == rtpLength && 0x24 === data[0]) {
        /* This is the beginning of a new RTP package */
        data.readByte();
        rtpChannel = data.readByte();
        rtpLength = data.readShort();
      }

      if (data.bytesAvailable < rtpLength) {
        /* The complete RTP package is not here yet, wait for more data */
        return;
      }

      var pkgData:ByteArray = new ByteArray();

      data.readBytes(pkgData, 0, rtpLength);

      if (rtpChannel === 0 || rtpChannel === 2) {
        /* We're discarding the RTCP counter parts for now */
        var rtppkt:RTP = new RTP(pkgData, sdp);
        dispatchEvent(rtppkt);
      }

      requestReset();

      if (0 < data.bytesAvailable) {
        onData(null);
      }
    }

    private function supportCommand(command:String):Boolean
    {
      return (-1 !== this.methods.indexOf(command));
    }

    private function sendOptionsReq():void {
      state = STATE_OPTIONS;
      var req:String =
        "OPTIONS * RTSP/1.0\r\n" +
        "CSeq: " + (++cSeq) + "\r\n" +
        "User-Agent: " + userAgent + "\r\n" +
        "\r\n";
      handle.writeUTFBytes(req);
    }

    private function sendDescribeReq():void {
      state = STATE_DESCRIBE;
      var u:String = 'rtsp://' + urlParsed.host + ":" + urlParsed.port + urlParsed.urlpath;
      var req:String =
        "DESCRIBE " + u + " RTSP/1.0\r\n" +
        "CSeq: " + (++cSeq) + "\r\n" +
        "User-Agent: " + userAgent + "\r\n" +
        "Accept: application/sdp\r\n" +
        auth.authorizationHeader("DESCRIBE", authState, authOpts, urlParsed, digestNC++) +
        "\r\n";
      handle.writeUTFBytes(req);
    }

    private function sendSetupReq(block:Object):void {
      state = STATE_SETUP;
      var interleavedChannels:String = interleaveChannelIndex++ + "-" + interleaveChannelIndex++;
      var p:String = url.isAbsolute(block.control) ? block.control : contentBase + block.control;

      trace('Setting up track: ' + p);
      var req:String =
        "SETUP " + p + " RTSP/1.0\r\n" +
        "CSeq: " + (++cSeq) + "\r\n" +
        "User-Agent: " + userAgent + "\r\n" +
        (session ? ("Session: " + session + "\r\n") : "") +
        "Transport: RTP/AVP/TCP;unicast;interleaved=" + interleavedChannels + "\r\n" +
        auth.authorizationHeader("SETUP", authState, authOpts, urlParsed, digestNC++) +
        "Date: " + new Date().toUTCString() + "\r\n" +
        "\r\n";
      handle.writeUTFBytes(req);
    }

    private function sendPlayReq():void {
      /* Put the NetStream in 'Data Generation Mode'. Data is generated by FLVMux */
      this.ns.play(null);

      state = STATE_PLAY;

      var req:String =
        "PLAY " + contentBase + " RTSP/1.0\r\n" +
        "CSeq: " + (++cSeq) + "\r\n" +
        "User-Agent: " + userAgent + "\r\n" +
        "Session: " + session + "\r\n" +
        auth.authorizationHeader("PLAY", authState, authOpts, urlParsed, digestNC++) +
        "\r\n";
      handle.writeUTFBytes(req);
    }

    private function sendPauseReq():void {
      if (-1 === this.supportCommand("PAUSE")) {
        throw new Error('Pause is not supported by server.');
      }

      state = STATE_PAUSE;

      /* NetStream must be closed here, otherwise it will think of this rtsp pause
         as a very bad connection and buffer a lot before playing again. Not
         excellent for live data. */
      this.ns.close();

      var req:String =
        "PAUSE " + contentBase + " RTSP/1.0\r\n" +
        "CSeq: " + (++cSeq) + "\r\n" +
        "User-Agent: " + userAgent + "\r\n" +
        "Session: " + session + "\r\n" +
        auth.authorizationHeader("PAUSE", authState, authOpts, urlParsed, digestNC++) +
        "\r\n";
      handle.writeUTFBytes(req);
    }

    private function sendTeardownReq():void {
      state = STATE_TEARDOWN;
      var req:String =
        "TEARDOWN " + contentBase + " RTSP/1.0\r\n" +
        "CSeq: " + (++cSeq) + "\r\n" +
        "User-Agent: " + userAgent + "\r\n" +
        "Session: " + session + "\r\n" +
        auth.authorizationHeader("TEARDOWN", authState, authOpts, urlParsed, digestNC++) +
        "\r\n";
      handle.writeUTFBytes(req);
    }
  }
}

package com.axis.rtspclient {

  import flash.events.EventDispatcher;
  import flash.events.Event;
  import flash.utils.ByteArray;
  import flash.net.Socket;
  import flash.net.NetConnection;
  import flash.net.NetStream;
  import flash.media.Video;
  import mx.utils.StringUtil;

  import com.axis.rtspclient.FLVMux;
  import com.axis.rtspclient.RTP;
  import com.axis.rtspclient.SDP;
  import com.axis.http.url;
  import com.axis.http.request;
  import com.axis.http.auth;
  import com.axis.IClient;
  import com.axis.ClientEvent;

  public class RTSPClient extends EventDispatcher implements IClient {
    [Embed(source = "../../../../VERSION", mimeType = "application/octet-stream")] private var Version:Class;
    private var userAgent:String;

    private static var STATE_INITIAL:uint  = 1 << 0;
    private static var STATE_OPTIONS:uint  = 1 << 1;
    private static var STATE_DESCRIBE:uint = 1 << 2;
    private static var STATE_SETUP:uint    = 1 << 3;
    private static var STATE_PLAY:uint     = 1 << 4;
    private static var STATE_PLAYING:uint  = 1 << 5;
    private static var STATE_PAUSE:uint    = 1 << 6;
    private static var STATE_PAUSED:uint   = 1 << 7;
    private static var STATE_TEARDOWN:uint = 1 << 8;

    private var state:int = STATE_INITIAL;
    private var handle:IRTSPHandle;
    private var ns:NetStream;
    private var video:Video;

    private var sdp:SDP = new SDP();
    private var flvmux:FLVMux;

    private var urlParsed:Object;
    private var cSeq:uint = 1;
    private var session:String;
    private var contentBase:String;
    private var interleaveChannelIndex:uint = 0;

    private var methods:Array = [];
    private var data:ByteArray = new ByteArray();
    private var rtpLength:int = -1;
    private var rtpChannel:int = -1;
    private var tracks:Array;

    private var authState:String = "none";
    private var authOpts:Object = {};
    private var digestNC:uint = 1;

    public function RTSPClient(video:Video, urlParsed:Object, handle:IRTSPHandle) {
      this.userAgent = "Slush " + StringUtil.trim(new Version().toString());
      this.state = STATE_INITIAL;
      this.handle = handle;
      this.ns = ns;
      this.video = video;
      this.urlParsed = urlParsed;

      handle.addEventListener('data', this.onData);
    }

    public function start():Boolean {
      var self:RTSPClient = this;
      handle.addEventListener('connected', function():void {
        if (state !== STATE_INITIAL) {
          trace('Cannot start unless in initial state.');
          return;
        }

        /* If the handle closes, take care of it */
        handle.addEventListener('closed', self.onClose);

        if (0 === self.methods.length) {
          /* We don't know the options yet. Start with that. */
          sendOptionsReq();
        } else {
          /* Already queried the options (and perhaps got unauthorized on describe) */
          sendDescribeReq();
        }
      });

      var nc:NetConnection = new NetConnection();
      nc.connect(null);
      this.ns = new NetStream(nc);
      dispatchEvent(new ClientEvent(ClientEvent.NETSTREAM_CREATED, { ns : this.ns }));
      video.attachNetStream(this.ns);

      handle.connect();
      return true;
    }

    public function pause():Boolean
    {
      if (state !== STATE_PLAYING) {
        trace('Unable to pause a stream if not playing.');
        return false;
      }

      try {
        sendPauseReq();
      } catch (err:Error) {
        trace("Unable to pause: " + err.message);
        return false;
      }

      return true;
    }

    public function resume():Boolean
    {
      if (state !== STATE_PAUSED) {
        trace('Unable to resume a stream if not paused.');
        return false;
      }

      sendPlayReq();
      return true;
    }

    public function stop():Boolean
    {
      if (state < STATE_PLAY) {
        trace('Unable to stop if we never reached play.');
        return false;
      }
      sendTeardownReq();
      return true;
    }

    private function onClose(event:Event):void
    {
      if (state === STATE_TEARDOWN) {
        dispatchEvent(new ClientEvent(ClientEvent.STOPPED));
      } else {
        trace('RTSPClient: Handle unexpectedly closed.');
      }
    }

    private function onData(event:Event):void
    {
      /* read one byte to determine destination */
      if (0 < data.bytesAvailable) {
        /* Determining byte have already been read. This is a continuation */
      } else {
        /* Read the determining byte */
        handle.readBytes(data, 0, 1);
      }

      switch(data[0]) {
        case 0x52:
          /* ascii 'R', start of RTSP */
          onRTSPCommand();
          break;

        case 0x24:
          /* ascii '$', start of interleaved packet */
          onInterleavedData();
          break;

        default:
          trace('Unknown determining byte:', data[0]);
          break;
      }
    }

    private function requestReset():void
    {
      var copy:ByteArray = new ByteArray();
      data.readBytes(copy);
      data.clear();
      copy.readBytes(data);

      rtpLength  = -1;
      rtpChannel = -1;
    }

    private function readRequest(oBody:ByteArray):*
    {
      var parsed:* = request.readHeaders(handle, data);
      if (false === parsed) {
        return false;
      }

      if (401 === parsed.code) {
        /* Unauthorized, change authState and (possibly) try again */
        authOpts = parsed.headers['www-authenticate'];
        var newAuthState:String = auth.nextMethod(authState, authOpts);
        if (authState === newAuthState) {
          trace('GET: Exhausted all authentication methods.');
          trace('GET: Unable to authorize to ' + urlParsed.host);
          return false;
        }

        trace('RTSPClient: switching http-authorization from ' + authState + ' to ' + newAuthState);
        authState = newAuthState;
        state = STATE_INITIAL;
        data = new ByteArray();
        handle.reconnect();
        return false;
      }

      if (data.bytesAvailable < parsed.headers['content-length']) {
        return false;
      }

      /* RTSP commands contain no heavy body, so it's safe to read everything */
      data.readBytes(oBody, 0, parsed.headers['content-length']);
      requestReset();
      return parsed;
    }

    private function onRTSPCommand():void {
      var parsed:*, body:ByteArray = new ByteArray();
      if (false === (parsed = readRequest(body))) {
        return;
      }

      if (200 !== parsed.code) {
        trace('RTSPClient: Invalid RTSP response - ', parsed.code, parsed.message);
        return;
      }

      switch (state) {
      case STATE_INITIAL:
        trace("RTSPClient: STATE_INITIAL");

      case STATE_OPTIONS:
        trace("RTSPClient: STATE_OPTIONS");
        this.methods = parsed.headers.public.split(/[ ]*,[ ]*/);
        sendDescribeReq();

        break;
      case STATE_DESCRIBE:
        trace("RTSPClient: STATE_DESCRIBE");

        if (!sdp.parse(body)) {
          trace("RTSPClient:Failed to parse SDP file");
          return;
        }

        if (!parsed.headers['content-base']) {
          trace('RTSPClient: no content-base in describe reply');
          return;
        }

        contentBase = parsed.headers['content-base'];
        tracks = sdp.getMediaBlockList();
        trace('SDP contained ' + tracks.length + ' track(s). Calling SETUP for each.');

        if (0 === tracks.length) {
          trace('No tracks in SDP file.');
          return;
        }

        /* Fall through, it's time for setup */
      case STATE_SETUP:
        trace("RTSPClient: STATE_SETUP");

        if (parsed.headers['session']) {
          session = parsed.headers['session'];
        }

        if (0 !== tracks.length) {
          /* More tracks we must setup before playing */
          var block:Object = tracks.shift();
          sendSetupReq(block);
          return;
        }

        /* All tracks setup and ready to go! */
        sendPlayReq();
        break;

      case STATE_PLAY:
        trace("RTSPClient: STATE_PLAY");
        state = STATE_PLAYING;

        if (this.flvmux) {
          /* If the flvmux have been initialized don't do it again.
             this is probably a resume after pause */
          break;
        }

        this.flvmux = new FLVMux(this.ns, this.sdp);
        var analu:ANALU = new ANALU();
        var aaac:AAAC = new AAAC(sdp);

        this.addEventListener("VIDEO_PACKET", analu.onRTPPacket);
        this.addEventListener("AUDIO_PACKET", aaac.onRTPPacket);
        analu.addEventListener(NALU.NEW_NALU, flvmux.onNALU);
        aaac.addEventListener(AACFrame.NEW_FRAME, flvmux.onAACFrame);
        break;

      case STATE_PLAYING:
        trace("RTSPClient: STATE_PLAYING");
        break;

      case STATE_PAUSE:
        trace("RTSPClient: STATE_PAUSE");
        state = STATE_PAUSED;
        break;

      case STATE_TEARDOWN:
        trace('RTSPClient: STATE_TEARDOWN');
        handle.disconnect();
        break;
      }

      if (0 < data.bytesAvailable) {
        onData(null);
      }
    }

    private function onInterleavedData():void
    {
      handle.readBytes(data, data.length);

      if (-1 == rtpLength && 0x24 === data[0]) {
        /* This is the beginning of a new RTP package */
        data.readByte();
        rtpChannel = data.readByte();
        rtpLength = data.readShort();
      }

      if (data.bytesAvailable < rtpLength) {
        /* The complete RTP package is not here yet, wait for more data */
        return;
      }

      var pkgData:ByteArray = new ByteArray();

      data.readBytes(pkgData, 0, rtpLength);

      if (rtpChannel === 0 || rtpChannel === 2) {
        /* We're discarding the RTCP counter parts for now */
        var rtppkt:RTP = new RTP(pkgData, sdp);
        dispatchEvent(rtppkt);
      }

      requestReset();

      if (0 < data.bytesAvailable) {
        onData(null);
      }
    }

    private function supportCommand(command:String):Boolean
    {
      return (-1 !== this.methods.indexOf(command));
    }

    private function sendOptionsReq():void {
      state = STATE_OPTIONS;
      var req:String =
        "OPTIONS * RTSP/1.0\r\n" +
        "CSeq: " + (++cSeq) + "\r\n" +
        "User-Agent: " + userAgent + "\r\n" +
        "\r\n";
      handle.writeUTFBytes(req);
    }

    private function sendDescribeReq():void {
      state = STATE_DESCRIBE;
      var u:String = 'rtsp://' + urlParsed.host + ":" + urlParsed.port + urlParsed.urlpath;
      var req:String =
        "DESCRIBE " + u + " RTSP/1.0\r\n" +
        "CSeq: " + (++cSeq) + "\r\n" +
        "User-Agent: " + userAgent + "\r\n" +
        "Accept: application/sdp\r\n" +
        auth.authorizationHeader("DESCRIBE", authState, authOpts, urlParsed, digestNC++) +
        "\r\n";
      handle.writeUTFBytes(req);
    }

    private function sendSetupReq(block:Object):void {
      state = STATE_SETUP;
      var interleavedChannels:String = interleaveChannelIndex++ + "-" + interleaveChannelIndex++;
      var p:String = url.isAbsolute(block.control) ? block.control : contentBase + block.control;

      trace('Setting up track: ' + p);
      var req:String =
        "SETUP " + p + " RTSP/1.0\r\n" +
        "CSeq: " + (++cSeq) + "\r\n" +
        "User-Agent: " + userAgent + "\r\n" +
        (session ? ("Session: " + session + "\r\n") : "") +
        "Transport: RTP/AVP/TCP;unicast;interleaved=" + interleavedChannels + "\r\n" +
        auth.authorizationHeader("SETUP", authState, authOpts, urlParsed, digestNC++) +
        "Date: " + new Date().toUTCString() + "\r\n" +
        "\r\n";
      handle.writeUTFBytes(req);
    }

    private function sendPlayReq():void {
      /* Put the NetStream in 'Data Generation Mode'. Data is generated by FLVMux */
      this.ns.play(null);

      state = STATE_PLAY;

      var req:String =
        "PLAY " + contentBase + " RTSP/1.0\r\n" +
        "CSeq: " + (++cSeq) + "\r\n" +
        "User-Agent: " + userAgent + "\r\n" +
        "Session: " + session + "\r\n" +
        auth.authorizationHeader("PLAY", authState, authOpts, urlParsed, digestNC++) +
        "\r\n";
      handle.writeUTFBytes(req);
    }

    private function sendPauseReq():void {
      if (-1 === this.supportCommand("PAUSE")) {
        throw new Error('Pause is not supported by server.');
      }

      state = STATE_PAUSE;

      /* NetStream must be closed here, otherwise it will think of this rtsp pause
         as a very bad connection and buffer a lot before playing again. Not
         excellent for live data. */
      this.ns.close();

      var req:String =
        "PAUSE " + contentBase + " RTSP/1.0\r\n" +
        "CSeq: " + (++cSeq) + "\r\n" +
        "User-Agent: " + userAgent + "\r\n" +
        "Session: " + session + "\r\n" +
        auth.authorizationHeader("PAUSE", authState, authOpts, urlParsed, digestNC++) +
        "\r\n";
      handle.writeUTFBytes(req);
    }

    private function sendTeardownReq():void {
      state = STATE_TEARDOWN;
      var req:String =
        "TEARDOWN " + contentBase + " RTSP/1.0\r\n" +
        "CSeq: " + (++cSeq) + "\r\n" +
        "User-Agent: " + userAgent + "\r\n" +
        "Session: " + session + "\r\n" +
        auth.authorizationHeader("TEARDOWN", authState, authOpts, urlParsed, digestNC++) +
        "\r\n";
      handle.writeUTFBytes(req);
    }
  }
}

Correct state debug output

RTSPClient: Correct state debug output


package org.mangui.HLS.muxing {

    import flash.utils.ByteArray;
    import org.mangui.HLS.utils.Log;

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

        /** H264 NAL unit names. **/
        private static const NAMES:Array = [
            'Unspecified',                  // 0
            'NDR',                          // 1
            'Partition A',                  // 2
            'Partition B',                  // 3
            'Partition C',                  // 4
            'IDR',                          // 5
            'SEI',                          // 6
            'SPS',                          // 7
            'PPS',                          // 8
            'AUD',                          // 9
            'End of Sequence',              // 10
            'End of Stream',                // 11
            'Filler Data'                   // 12
        ];
        /** H264 profiles. **/

        private static const PROFILES:Object = {
            '66': 'H264 Baseline',
            '77': 'H264 Main',
            '100': 'H264 High'
        };

        /** Get Avcc header from AVC stream
        See ISO 14496-15, 5.2.4.1 for the description of AVCDecoderConfigurationRecord
        **/
        public static function getAVCC(nalu:ByteArray,position:Number=0):ByteArray {
            // Find SPS and PPS units in AVC stream.
            var units:Vector.<VideoFrame> = AVC.getNALU(nalu,position);
            var sps:Number = -1;
            var pps:Number = -1;
            for(var i:Number = 0; i< units.length; i++) {
                if(units[i].type == 7 && sps == -1) {
                    sps = i;
                } else if (units[i].type == 8 && pps == -1) {
                    pps = i;
                }
            }
            // Throw errors if units not found.
            if(sps == -1) {
                throw new Error("No SPS NAL unit found in this stream.");
            } else if (pps == -1) {
                throw new Error("No PPS NAL unit found in this stream.");
            }
            // Write startbyte
            var avcc:ByteArray = new ByteArray();
            avcc.writeByte(0x01);
            // Write profile, compatibility and level.
            avcc.writeBytes(nalu,units[sps].start+1, 3);
            // reserved (6 bits), NALU length size - 1 (2 bits)
            avcc.writeByte(0xFC | 3);
            // reserved (3 bits), num of SPS (5 bits)
            avcc.writeByte(0xE0 | 1);
            // 2 bytes for length of SPS
            avcc.writeShort(units[sps].length);
            // data of SPS
            avcc.writeBytes(nalu,units[sps].start,units[sps].length);
            // Number of PPS
            avcc.writeByte(0x01);
            // 2 bytes for length of PPS
            avcc.writeShort(units[pps].length);
            // data of PPS
            avcc.writeBytes(nalu,units[pps].start,units[pps].length);
            // Grab profile/level
            avcc.position = 1;
            var prf:Number = avcc.readByte();
            avcc.position = 3;
            var lvl:Number = avcc.readByte();
            Log.debug("AVC: "+PROFILES[prf]+' level '+lvl);
            avcc.position = 0;
            return avcc;
        };

        /** Return an array with NAL delimiter indexes. **/
        public static function getNALU(nalu:ByteArray,position:Number=0):Vector.<VideoFrame> {
            var units:Vector.<VideoFrame> = new Vector.<VideoFrame>();
            var unit_start:Number;
            var unit_type:Number;
            var unit_header:Number;
            // Loop through data to find NAL startcodes.
            var window:uint = 0;
            nalu.position = position;
            while(nalu.bytesAvailable > 4) {
                window = nalu.readUnsignedInt();
                // Match four-byte startcodes
                if((window & 0xFFFFFFFF) == 0x01) {
                  // push previous NAL unit if new start delimiter found, dont push unit with type = 0
                    if(unit_start && unit_type) {
                        units.push(new VideoFrame(unit_header, nalu.position - 4 - unit_start, unit_start, unit_type));
                    }
                    unit_header = 4;
                    unit_start = nalu.position;
                    unit_type = nalu.readByte() & 0x1F;
                    // NDR or IDR NAL unit
                    if(unit_type == 1 || unit_type == 5) { break; }
                // Match three-byte startcodes
                } else if((window & 0xFFFFFF00) == 0x100) {
                    // push previous NAL unit if new start delimiter found, dont push unit with type = 0
                    if(unit_start && unit_type) {
                       units.push(new VideoFrame(unit_header, nalu.position - 4 - unit_start, unit_start, unit_type));
                    }
                    nalu.position--;
                    unit_header = 3;
                    unit_start = nalu.position;
                    unit_type = nalu.readByte() & 0x1F;
                    // NDR or IDR NAL unit
                    if(unit_type == 1 || unit_type == 5) { break; }
                } else {
                    nalu.position -= 3;
                }
            }
            // Append the last NAL to the array.
            if(unit_start) {
               units.push(new VideoFrame(unit_header, nalu.length - unit_start, unit_start, unit_type));
            }
            // Reset position and return results.
            if(Log.LOG_DEBUG2_ENABLED) {
                if(units.length) {
                    var txt:String = "AVC: ";
                    for(var i:Number=0; i<units.length; i++) {
                        txt += NAMES[units[i].type] + ", ";
                    }
                    Log.debug2(txt.substr(0,txt.length-2) + " slices");
                } else {
                    Log.debug2('AVC: no NALU slices found');
                }
            }
            nalu.position = position;
            return units;
        };

    }

}

package org.mangui.HLS.muxing {

    import flash.utils.ByteArray;
    import org.mangui.HLS.utils.Log;

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

        /** H264 NAL unit names. **/
        private static const NAMES:Array = [
            'Unspecified',                  // 0
            'NDR',                          // 1
            'Partition A',                  // 2
            'Partition B',                  // 3
            'Partition C',                  // 4
            'IDR',                          // 5
            'SEI',                          // 6
            'SPS',                          // 7
            'PPS',                          // 8
            'AUD',                          // 9
            'End of Sequence',              // 10
            'End of Stream',                // 11
            'Filler Data'                   // 12
        ];
        /** H264 profiles. **/

        private static const PROFILES:Object = {
            '66': 'H264 Baseline',
            '77': 'H264 Main',
            '100': 'H264 High'
        };

        /** Get Avcc header from AVC stream
        See ISO 14496-15, 5.2.4.1 for the description of AVCDecoderConfigurationRecord
        **/
        public static function getAVCC(nalu:ByteArray,position:Number):ByteArray {
            // Find SPS and PPS units in AVC stream.
            var units:Vector.<VideoFrame> = AVC.getNALU(nalu,position);
            var sps:Number = -1;
            var pps:Number = -1;
            for(var i:Number = 0; i< units.length; i++) {
                if(units[i].type == 7 && sps == -1) {
                    sps = i;
                } else if (units[i].type == 8 && pps == -1) {
                    pps = i;
                }
            }
            // Throw errors if units not found.
            if(sps == -1) {
                throw new Error("No SPS NAL unit found in this stream.");
            } else if (pps == -1) {
                throw new Error("No PPS NAL unit found in this stream.");
            }
            // Write startbyte
            var avcc:ByteArray = new ByteArray();
            avcc.writeByte(0x01);
            // Write profile, compatibility and level.
            avcc.writeBytes(nalu,units[sps].start+1, 3);
            // reserved (6 bits), NALU length size - 1 (2 bits)
            avcc.writeByte(0xFC | 3);
            // reserved (3 bits), num of SPS (5 bits)
            avcc.writeByte(0xE0 | 1);
            // 2 bytes for length of SPS
            avcc.writeShort(units[sps].length);
            // data of SPS
            avcc.writeBytes(nalu,units[sps].start,units[sps].length);
            // Number of PPS
            avcc.writeByte(0x01);
            // 2 bytes for length of PPS
            avcc.writeShort(units[pps].length);
            // data of PPS
            avcc.writeBytes(nalu,units[pps].start,units[pps].length);
            // Grab profile/level
            avcc.position = 1;
            var prf:Number = avcc.readByte();
            avcc.position = 3;
            var lvl:Number = avcc.readByte();
            Log.debug("AVC: "+PROFILES[prf]+' level '+lvl);
            avcc.position = 0;
            return avcc;
        };

        /** Return an array with NAL delimiter indexes. **/
        public static function getNALU(nalu:ByteArray,position:Number):Vector.<VideoFrame> {
            var units:Vector.<VideoFrame> = new Vector.<VideoFrame>();
            var unit_start:Number;
            var unit_type:Number;
            var unit_header:Number;
            // Loop through data to find NAL startcodes.
            var window:uint = 0;
            nalu.position = position;
            while(nalu.bytesAvailable > 4) {
                window = nalu.readUnsignedInt();
                // Match four-byte startcodes
                if((window & 0xFFFFFFFF) == 0x01) {
                  // push previous NAL unit if new start delimiter found, dont push unit with type = 0
                    if(unit_start && unit_type) {
                        units.push(new VideoFrame(unit_header, nalu.position - 4 - unit_start, unit_start, unit_type));
                    }
                    unit_header = 4;
                    unit_start = nalu.position;
                    unit_type = nalu.readByte() & 0x1F;
                    // NDR or IDR NAL unit
                    if(unit_type == 1 || unit_type == 5) { break; }
                // Match three-byte startcodes
                } else if((window & 0xFFFFFF00) == 0x100) {
                    // push previous NAL unit if new start delimiter found, dont push unit with type = 0
                    if(unit_start && unit_type) {
                       units.push(new VideoFrame(unit_header, nalu.position - 4 - unit_start, unit_start, unit_type));
                    }
                    nalu.position--;
                    unit_header = 3;
                    unit_start = nalu.position;
                    unit_type = nalu.readByte() & 0x1F;
                    // NDR or IDR NAL unit
                    if(unit_type == 1 || unit_type == 5) { break; }
                } else {
                    nalu.position -= 3;
                }
            }
            // Append the last NAL to the array.
            if(unit_start) {
               units.push(new VideoFrame(unit_header, nalu.length - unit_start, unit_start, unit_type));
            }
            // Reset position and return results.
            if(Log.LOG_DEBUG2_ENABLED) {
                if(units.length) {
                    var txt:String = "AVC: ";
                    for(var i:Number=0; i<units.length; i++) {
                        txt += NAMES[units[i].type] + ", ";
                    }
                    Log.debug2(txt.substr(0,txt.length-2) + " slices");
                } else {
                    Log.debug2('AVC: no NALU slices found');
                }
            }
            nalu.position = position;
            return units;
        };

    }

}

remove useless optional parameter values


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

package com.threerings.util {

import flash.utils.getQualifiedClassName;

/**
 * A simple logging mechanism.
 *
 * Log instances are created for modules, and the logging level can be configured per
 * module in a hierarchical fashion.
 *
 * Typically, you should create a module name based on the full path to a class:
 * calling getLog() and passing an object or Class will do this. Alternattely, you
 * may create a Log to share in several classes in a package, in which case the
 * module name can be like "com.foocorp.games.bunnywar". Finally, you can just
 * create made-up module names like "mygame" or "util", but this is not recommended.
 * You really should name things based on your packages, and your packages should be
 * named according to Sun's recommendations for Java packages.
 *
 * Typical usage for creating a Log to be used by the entire class would be:
 * public class MyClass
 * {
 *     private static const log :Log = Log.getLog(MyClass);
 *     ...
 *
 * OR, if you just need a one-off Log:
 *     protected function doStuff (thingy :Thingy) :void
 *     {
 *          if (!isValid(thingy)) {
 *              Log.getLog(this).warn("Invalid thingy specified", "thingy", thingy);
 *              ....
 */
public class Log
{
    /** Log level constants. */
    public static const DEBUG :int = 0;
    public static const INFO :int = 1;
    public static const WARNING :int = 2;
    public static const OFF :int = 3;
    // if you add to this, update LEVEL_NAMES and stringToLevel() at the bottom...

    /**
     * Retrieve a Log for the specified module.
     *
     * @param moduleSpec can be a String of the module name, or any Object or Class to
     * have the module name be the full package and name of the class (recommended).
     */
    public static function getLog (moduleSpec :*) :Log
    {
        const module :String = (moduleSpec is String) ? String(moduleSpec)
            : getQualifiedClassName(moduleSpec).replace("::", ".");
        return new Log(module);
    }

    /**
     * A convenience function for quickly and easily inserting printy
     * statements during application development.
     */
    public static function testing (... params) :void
    {
        var log :Log = new Log("testing");
        log.debug.apply(log, params);
    }

    /**
     * A convenience function for quickly printing a stack trace
     * to the log, useful for debugging.
     */
    public static function dumpStack () :void
    {
        testing(new Error("dumpStack").getStackTrace());
    }

    /**
     * Add a logging target.
     */
    public static function addTarget (target :LogTarget) :void
    {
        _targets.push(target);
    }

    /**
     * Remove a logging target.
     */
    public static function removeTarget (target :LogTarget) :void
    {
        var dex :int = _targets.indexOf(target);
        if (dex != -1) {
            _targets.splice(dex, 1);
        }
    }

    /**
     * Set the log level for the specified module.
     *
     * @param module The smallest prefix desired to configure a log level.
     * For example, you can set the global level with Log.setLevel("", Log.INFO);
     * Then you can Log.setLevel("com.foo.game", Log.DEBUG). Now, everything
     * logs at INFO level except for modules within com.foo.game, which is at DEBUG.
     */
    public static function setLevel (module :String, level :int) :void
    {
        _setLevels[module] = level;
        _levels = {}; // reset cached levels
    }

    /**
     * Parses a String in the form of ":info;com.foo.game:debug;com.bar.util:warning"
     *
     * Semicolons separate modules, colons separate a module name from the log level.
     * An empty string specifies the top-level (global) module.
     */
    public static function setLevels (settingString :String) :void
    {
        for each (var module :String in settingString.split(";")) {
            var setting :Array = module.split(":");
            _setLevels[setting[0]] = stringToLevel(String(setting[1]));
        }
        _levels = {}; // reset cached levels
    }

    /**
     * Use Log.getLog();
     *
     * @private
     */
    public function Log (module :String)
    {
        if (module == null) module = "";
        _module = module;
    }

    /**
     * Log a message with 'debug' priority.
     *
     * @param args The first argument is the actual message to log. After that, each pair
     * of parameters is printed in key/value form, the benefit being that if no log
     * message is generated then toString() will not be called on the values.
     * A final parameter may be an Error, in which case the stack trace is printed.
     *
     * @example
     * <listing version="3.0">
     *    log.debug("Message", "key1", value1, "key2", value2, optionalError);
     * </listing>
     */
    public function debug (... args) :void
    {
        doLog(DEBUG, args);
    }

    /**
     * Log a message with 'info' priority.
     *
     * @param args The first argument is the actual message to log. After that, each pair
     * of parameters is printed in key/value form, the benefit being that if no log
     * message is generated then toString() will not be called on the values.
     * A final parameter may be an Error, in which case the stack trace is printed.
     *
     * @example
     * <listing version="3.0">
     *    log.info("Message", "key1", value1, "key2", value2, optionalError);
     * </listing>
     */
    public function info (... args) :void
    {
        doLog(INFO, args);
    }

    /**
     * Log a message with 'warning' priority.
     *
     * @param args The first argument is the actual message to log. After that, each pair
     * of parameters is printed in key/value form, the benefit being that if no log
     * message is generated then toString() will not be called on the values.
     * A final parameter may be an Error, in which case the stack trace is printed.
     *
     * @example
     * <listing version="3.0">
     *    log.warning("Message", "key1", value1, "key2", value2, optionalError);
     * </listing>
     */
    public function warning (... args) :void
    {
        doLog(WARNING, args);
    }

    /**
     * Log just a stack trace with 'warning' priority.
     */
    public function logStackTrace (error :Error) :void
    {
        warning(error.getStackTrace());
    }

    protected function doLog (level :int, args :Array) :void
    {
        if (level < getLevel(_module)) {
            return; // we don't want to log it!
        }
        var logMessage :String = formatMessage(level, args);
        trace(logMessage);
        // possibly also dispatch to any other log targets.
        for each (var target :LogTarget in _targets) {
            target.log(logMessage);
        }
    }

    protected function formatMessage (level :int, args :Array) :String
    {
        var msg :String = getTimeStamp() + " " + LEVEL_NAMES[level] + ": " + _module;
        if (args.length > 0) {
            msg += " " + String(args[0]); // the primary log message
            var err :Error = null;
            if (args.length % 2 == 0) { // there's one extra arg
                var lastArg :Object = args.pop();
                if (lastArg is Error) {
                    err = lastArg as Error; // ok, it's an error, we like those
                } else {
                    args.push(lastArg, ""); // what? Well, cope by pushing it back with a ""
                }
            }
            if (args.length > 1) {
                for (var ii :int = 1; ii < args.length; ii += 2) {
                    msg += (ii == 1) ? " [" : ", ";
                    msg += String(args[ii]) + "=" + String(args[ii + 1]);
                }
                msg += "]";
            }
            if (err != null) {
                msg += "\n" + err.getStackTrace();
            }
        }
        return msg;
    }

    protected function getTimeStamp () :String
    {
        var d :Date = new Date();
        // return d.toLocaleTimeString();

        // format it like the date format in our java logs
        return d.fullYear + "/" +
            StringUtil.prepad(String(d.month + 1), 2, "0") + "/" +
            StringUtil.prepad(String(d.date), 2, "0") + " " +
            StringUtil.prepad(String(d.hours), 2, "0") + ":" +
            StringUtil.prepad(String(d.minutes), 2, "0") + ":" +
            StringUtil.prepad(String(d.seconds), 2, "0") + "," +
            StringUtil.prepad(String(d.milliseconds), 3, "0");
    }

    /**
     * Get the logging level for the specified module.
     */
    protected static function getLevel (module :String) :int
    {
        // we probably already have the level cached for this module
        var lev :Object = _levels[module];
        if (lev == null) {
            // cache miss- copy some parent module's level...
            var ancestor :String = module;
            while (true) {
                lev = _setLevels[ancestor];
                if (lev != null || ancestor == "") {
                    // bail if we found a setting or get to the top level,
                    // but always save the level from _setLevels into _levels
                    _levels[module] = int(lev); // if lev was null, this will become 0 (DEBUG)
                    break;
                }
                var dex :int = ancestor.lastIndexOf(".");
                ancestor = (dex == -1) ? "" : ancestor.substring(0, dex);
            }
        }
        return int(lev);
    }

    protected static function stringToLevel (s :String) :int
    {
        switch (s.toLowerCase()) {
        default: // default to DEBUG
        case "debug": return DEBUG;
        case "info": return INFO;
        case "warning": case "warn": return WARNING;
        case "off": return OFF;
        }
    }

    /** The module to which this log instance applies. */
    protected var _module :String;

    /** Other registered LogTargets, besides the trace log. */
    protected static var _targets :Array = [];

    /** A cache of log levels, copied from _setLevels. */
    protected static var _levels :Object = {};

    /** The configured log levels. */
    protected static var _setLevels :Object = { "": DEBUG }; // global: debug

    /** The names of each level. The last one isn't used, it corresponds with OFF. */
    protected static const LEVEL_NAMES :Array = [ "[debug]", "[INFO]", "[WARNING]", false ];
}
}

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

package com.threerings.util {

import flash.utils.getQualifiedClassName;

/**
 * A simple logging mechanism.
 *
 * Log instances are created for modules, and the logging level can be configured per
 * module in a hierarchical fashion.
 *
 * Typically, you should create a module name based on the full path to a class:
 * calling getLog() and passing an object or Class will do this. Alternattely, you
 * may create a Log to share in several classes in a package, in which case the
 * module name can be like "com.foocorp.games.bunnywar". Finally, you can just
 * create made-up module names like "mygame" or "util", but this is not recommended.
 * You really should name things based on your packages, and your packages should be
 * named according to Sun's recommendations for Java packages.
 *
 * Typical usage for creating a Log to be used by the entire class would be:
 * public class MyClass
 * {
 *     private static const log :Log = Log.getLog(MyClass);
 *     ...
 *
 * OR, if you just need a one-off Log:
 *     protected function doStuff (thingy :Thingy) :void
 *     {
 *          if (!isValid(thingy)) {
 *              Log.getLog(this).warn("Invalid thingy specified", "thingy", thingy);
 *              ....
 */
public class Log
{
    /** Log level constants. */
    public static const DEBUG :int = 0;
    public static const INFO :int = 1;
    public static const WARNING :int = 2;
    public static const OFF :int = 3;
    // if you add to this, update LEVEL_NAMES and stringToLevel() at the bottom...

    /**
     * Retrieve a Log for the specified module.
     *
     * @param moduleSpec can be a String of the module name, or any Object or Class to
     * have the module name be the full package and name of the class (recommended).
     */
    public static function getLog (moduleSpec :*) :Log
    {
        const module :String = (moduleSpec is String) ? String(moduleSpec)
            : getQualifiedClassName(moduleSpec).replace("::", ".");
        return new Log(module);
    }

    /**
     * A convenience function for quickly and easily inserting printy
     * statements during application development.
     */
    public static function testing (... params) :void
    {
        var log :Log = new Log("testing");
        log.debug.apply(log, params);
    }

    /**
     * A convenience function for quickly printing a stack trace
     * to the log, useful for debugging.
     */
    public static function dumpStack () :void
    {
        testing(new Error("dumpStack").getStackTrace());
    }

    /**
     * Add a logging target.
     */
    public static function addTarget (target :LogTarget) :void
    {
        _targets.push(target);
    }

    /**
     * Remove a logging target.
     */
    public static function removeTarget (target :LogTarget) :void
    {
        var dex :int = _targets.indexOf(target);
        if (dex != -1) {
            _targets.splice(dex, 1);
        }
    }

    /**
     * Set the log level for the specified module.
     *
     * @param module The smallest prefix desired to configure a log level.
     * For example, you can set the global level with Log.setLevel("", Log.INFO);
     * Then you can Log.setLevel("com.foo.game", Log.DEBUG). Now, everything
     * logs at INFO level except for modules within com.foo.game, which is at DEBUG.
     */
    public static function setLevel (module :String, level :int) :void
    {
        _setLevels[module] = level;
        _levels = {}; // reset cached levels
    }

    /**
     * Parses a String in the form of ":info;com.foo.game:debug;com.bar.util:warning"
     *
     * Semicolons separate modules, colons separate a module name from the log level.
     * An empty string specifies the top-level (global) module.
     */
    public static function setLevels (settingString :String) :void
    {
        for each (var module :String in settingString.split(";")) {
            var setting :Array = module.split(":");
            _setLevels[setting[0]] = stringToLevel(String(setting[1]));
        }
        _levels = {}; // reset cached levels
    }

    /**
     * Use Log.getLog();
     *
     * @private
     */
    public function Log (module :String)
    {
        if (module == null) module = "";
        _module = module;
    }

    /**
     * Log a message with 'debug' priority.
     *
     * @param args The first argument is the actual message to log. After that, each pair
     * of parameters is printed in key/value form, the benefit being that if no log
     * message is generated then toString() will not be called on the values.
     * A final parameter may be an Error, in which case the stack trace is printed.
     *
     * @example
     * <listing version="3.0">
     *    log.debug("Message", "key1", value1, "key2", value2, optionalError);
     * </listing>
     */
    public function debug (... args) :void
    {
        doLog(DEBUG, args);
    }

    /**
     * Log a message with 'info' priority.
     *
     * @param args The first argument is the actual message to log. After that, each pair
     * of parameters is printed in key/value form, the benefit being that if no log
     * message is generated then toString() will not be called on the values.
     * A final parameter may be an Error, in which case the stack trace is printed.
     *
     * @example
     * <listing version="3.0">
     *    log.info("Message", "key1", value1, "key2", value2, optionalError);
     * </listing>
     */
    public function info (... args) :void
    {
        doLog(INFO, args);
    }

    /**
     * Log a message with 'warning' priority.
     *
     * @param args The first argument is the actual message to log. After that, each pair
     * of parameters is printed in key/value form, the benefit being that if no log
     * message is generated then toString() will not be called on the values.
     * A final parameter may be an Error, in which case the stack trace is printed.
     *
     * @example
     * <listing version="3.0">
     *    log.warning("Message", "key1", value1, "key2", value2, optionalError);
     * </listing>
     */
    public function warning (... args) :void
    {
        doLog(WARNING, args);
    }

    /**
     * Log just a stack trace with 'warning' priority.
     */
    public function logStackTrace (error :Error) :void
    {
        warning(error.getStackTrace());
    }

    protected function doLog (level :int, args :Array) :void
    {
        if (level < getLevel(_module)) {
            return; // we don't want to log it!
        }
        var logMessage :String = formatMessage(level, args);
        trace(logMessage);
        // possibly also dispatch to any other log targets.
        for each (var target :LogTarget in _targets) {
            target.log(logMessage);
        }
    }

    protected function formatMessage (level :int, args :Array) :String
    {
        var msg :String = getTimeStamp() + " " + LEVEL_NAMES[level] + ": " + _module;
        if (args.length > 0) {
            msg += " " + String(args[0]); // the primary log message
            var err :Error = null;
            if (args.length % 2 == 0) { // there's one extra arg
                var lastArg :Object = args.pop();
                if (lastArg is Error) {
                    err = lastArg as Error; // ok, it's an error, we like those
                } else {
                    args.push(lastArg, ""); // what? Well, cope by pushing it back with a ""
                }
            }
            if (args.length > 1) {
                for (var ii :int = 1; ii < args.length; ii += 2) {
                    msg += (ii == 1) ? " [" : ", ";
                    msg += String(args[ii]) + "=" + String(args[ii + 1]);
                }
                msg += "]";
            }
            if (err != null) {
                msg += "\n" + err.getStackTrace();
            }
        }
        return msg;
    }

    protected function getTimeStamp () :String
    {
        var d :Date = new Date();
        // return d.toLocaleTimeString();

        // format it like the date format in our java logs
        return d.fullYear + "/" +
            StringUtil.prepad(String(d.month + 1), 2, "0") + "/" +
            StringUtil.prepad(String(d.date), 2, "0") + " " +
            StringUtil.prepad(String(d.hours), 2, "0") + ":" +
            StringUtil.prepad(String(d.minutes), 2, "0") + ":" +
            StringUtil.prepad(String(d.seconds), 2, "0") + "," +
            StringUtil.prepad(String(d.milliseconds), 3, "0");
    }

    /**
     * Get the logging level for the specified module.
     */
    protected static function getLevel (module :String) :int
    {
        // we probably already have the level cached for this module
        var lev :Object = _levels[module];
        if (lev == null) {
            // cache miss- copy some parent module's level...
            var ancestor :String = module;
            while (true) {
                lev = _setLevels[ancestor];
                if (lev != null || ancestor == "") {
                    // bail if we found a setting or get to the top level,
                    // but always save the level from _setLevels into _levels
                    _levels[module] = int(lev); // if lev was null, this will become 0 (DEBUG)
                    break;
                }
                var dex :int = ancestor.lastIndexOf(".");
                ancestor = (dex == -1) ? "" : ancestor.substring(0, dex);
            }
        }
        return int(lev);
    }

    protected static function stringToLevel (s :String) :int
    {
        switch (s.toLowerCase()) {
        default: // default to DEBUG
        case "debug": return DEBUG;
        case "info": return INFO;
        case "warning": case "warn": return WARNING;
        case "off": return OFF;
        }
    }

    /** The module to which this log instance applies. */
    protected var _module :String;

    /** Other registered LogTargets, besides the trace log. */
    protected static var _targets :Array = [];

    /** A cache of log levels, copied from _setLevels. */
    protected static var _levels :Object = {};

    /** The configured log levels. */
    protected static var _setLevels :Object = { "": DEBUG }; // global: debug

    /** The outputted names of each level. The last one isn't used, it corresponds with OFF. */
    protected static const LEVEL_NAMES :Array = [ "debug", "INFO", "WARNING", false ];
}
}

Stop printing square brackets around the log level. Be more like our Java logging.

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


package com.mozaic{

	public class mozaic {

		public function mozaic() {
			// constructor code
			trace('The work has begun');
		}

		//do we need this?
		public function getCSV(path:String){
			//holds the csv data
			var details:Object = new Object();
			return(details);
		}

		public function processImage(img_id:String){

		}

	}

}

test commit

test commit


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

package org.flintparticles.threeD.emitters
{
	import org.flintparticles.common.emitters.Emitter;
	import org.flintparticles.common.particles.Particle;
	import org.flintparticles.common.particles.ParticleFactory;
	import org.flintparticles.threeD.geom.Quaternion;
	import org.flintparticles.threeD.particles.Particle3D;
	import org.flintparticles.threeD.particles.ParticleCreator3D;

	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;

	/**
	 * The Emitter3D class defines an emitter that exists in 3D space. It is the
	 * main emitter for using Flint in a 3D coordinate system.
	 *
	 *
This emitter is not constrained to any particular 3D rendering
	 * environment. The emitter manages the creation and updating of each
	 * particle's state but does not do any rendering of the particles.
	 * The rendering is handled by a renderer. Renderer's could be created
	 * for drawing an emitter's particles on any 3D drawing environment.

	 *
	 *
This emitter adds 3D specific features to the base emitter class.

	 */

	public class Emitter3D extends Emitter
	{
		/**
		 * @private
		 *
		 * default factory to manage the creation, reuse and destruction of particles
		 */
		protected static var _creator:ParticleCreator3D = new ParticleCreator3D();

		/**
		 * The default particle factory used to manage the creation, reuse and destruction of particles.
		 */
		public static function get defaultParticleFactory():ParticleFactory
		{
			return _creator;
		}

		/**
		 * @private
		 */
		protected var _position:Vector3D;
		/**
		 * @private
		 */
		protected var _rotation:Quaternion;
		/**
		 * @private
		 */
		protected var _rotationTransform:Matrix3D;
		private var _rotTransformRotation:Quaternion;

		/**
		 * Identifies whether the particles should be arranged
		 * into a spacially sorted array - this speeds up proximity
		 * testing for those actions that need it.
		 */
		public var spaceSort:Boolean = false;

		/**
		 * The constructor creates an emitter 3D.
		 */
		public function Emitter3D()
		{
			super();
			_particleFactory = _creator;
			_position = new Vector3D( 0, 0, 0, 1 );
			_rotation = Quaternion.IDENTITY.clone();
			_rotationTransform = new Matrix3D();
			_rotTransformRotation = Quaternion.IDENTITY.clone();
		}

		/**
		 * Indicates the position of the Emitter instance relative to
		 * the local coordinate system of the Renderer.
		 */
		public function get position():Vector3D
		{
			return _position;
		}
		public function set position( value:Vector3D ):void
		{
			_position = value;
			_position.w = 1;
		}
		/**
		 * Indicates the rotation of the Emitter instance relative to
		 * the local coordinate system of the Renderer.
		 */
		public function get rotation():Quaternion
		{
			return _rotation;
		}
		public function set rotation( value:Quaternion ):void
		{
			_rotation = value;
		}

		/**
		 * Indicates the rotation of the Emitter instance relative to
		 * the local coordinate system of the Renderer, as a matrix
		 * transformation.
		 */
		public function get rotationTransform():Matrix3D
		{
			if( !_rotTransformRotation.equals( _rotation ) )
			{
				_rotationTransform = _rotation.toMatrixTransformation();
				_rotTransformRotation = _rotation.clone();
			}
			return _rotationTransform;
		}

		/*
		 * Used internally to initialize a particle based on the state of the
		 * emitter. This function sets the particle's position and rotation to
		 * match the position and rotation of the emitter. After setting this
		 * default state, the initializer actions will be applied to the particle.
		 *
		 * @param particle The particle to initialize.
		 */
		override protected function initParticle( particle:Particle ):void
		{
			var p:Particle3D = Particle3D( particle );
			p.position = _position.clone();
			p.rotation = _rotation.clone();
		}

		/**
		 * If the spaceSort property is true, this method creates the spaceSortedX
		 * array.
		 *
		 * @param time The duration, in seconds, of the current frame.
		 */
		override protected function sortParticles():void
		{
			if( spaceSort )
			{
				_particles.sortOn( "x", Array.NUMERIC );
				var len:int = _particles.length;
				for( var i:int = 0; i < len; ++i )
				{
					Particle3D( _particles[ i ] ).sortID = i;
				}
			}
		}
	}
}

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

package org.flintparticles.threeD.emitters
{
	import org.flintparticles.common.emitters.Emitter;
	import org.flintparticles.common.particles.Particle;
	import org.flintparticles.common.particles.ParticleFactory;
	import org.flintparticles.threeD.geom.Quaternion;
	import org.flintparticles.threeD.particles.Particle3D;
	import org.flintparticles.threeD.particles.ParticleCreator3D;

	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;

	/**
	 * The Emitter3D class defines an emitter that exists in 3D space. It is the
	 * main emitter for using Flint in a 3D coordinate system.
	 *
	 *
This emitter is not constrained to any particular 3D rendering
	 * environment. The emitter manages the creation and updating of each
	 * particle's state but does not do any rendering of the particles.
	 * The rendering is handled by a renderer. Renderer's could be created
	 * for drawing an emitter's particles on any 3D drawing environment.

	 *
	 *
This emitter adds 3D specific features to the base emitter class.

	 */

	public class Emitter3D extends Emitter
	{
		/**
		 * @private
		 *
		 * default factory to manage the creation, reuse and destruction of particles
		 */
		protected static var _creator:ParticleCreator3D = new ParticleCreator3D();

		/**
		 * The default particle factory used to manage the creation, reuse and destruction of particles.
		 */
		public static function get defaultParticleFactory():ParticleFactory
		{
			return _creator;
		}

		/**
		 * @private
		 */
		protected var _position:Vector3D;
		/**
		 * @private
		 */
		protected var _rotation:Quaternion;
		/**
		 * @private
		 */
		protected var _rotationTransform:Matrix3D;
		private var _rotTransformRotation:Quaternion;

		/**
		 * Identifies whether the particles should be arranged
		 * into a spacially sorted array - this speeds up proximity
		 * testing for those actions that need it.
		 */
		public var spaceSort:Boolean = false;

		/**
		 * The constructor creates an emitter 3D.
		 */
		public function Emitter3D()
		{
			super();
			_particleFactory = _creator;
			_position = new Vector3D( 0, 0, 0, 1 );
			_rotation = Quaternion.IDENTITY.clone();
			_rotationTransform = new Matrix3D();
			_rotTransformRotation = Quaternion.IDENTITY.clone();
		}

		/**
		 * Indicates the position of the Emitter instance relative to
		 * the local coordinate system of the Renderer.
		 */
		public function get position():Vector3D
		{
			return _position;
		}
		public function set position( value:Vector3D ):void
		{
			_position = value;
			_position.w = 1;
		}
		/**
		 * Indicates the rotation of the Emitter instance relative to
		 * the local coordinate system of the Renderer.
		 */
		public function get rotation():Quaternion
		{
			return _rotation;
		}
		public function set rotation( value:Quaternion ):void
		{
			_rotation = value;
		}

		/**
		 * Indicates the rotation of the Emitter instance relative to
		 * the local coordinate system of the Renderer, as a matrix
		 * transformation.
		 */
		public function get rotationTransform():Matrix3D
		{
			if( !_rotTransformRotation.equals( _rotation ) )
			{
				_rotationTransform = _rotation.toMatrixTransformation();
				_rotTransformRotation = _rotation.clone();
			}
			return _rotationTransform;
		}

		/*
		 * Used internally to initialize a particle based on the state of the
		 * emitter. This function sets the particle's position and rotation to
		 * match the position and rotation of the emitter. After setting this
		 * default state, the initializer actions will be applied to the particle.
		 *
		 * @param particle The particle to initialize.
		 */
		override protected function initParticle( particle:Particle ):void
		{
			var p:Particle3D = Particle3D( particle );
			p.position.x = _position.x;
			p.position.y = _position.y;
			p.position.z = _position.z;
			p.rotation.w = _rotation.w;
			p.rotation.x = _rotation.x;
			p.rotation.y = _rotation.y;
			p.rotation.z = _rotation.z;
		}

		/**
		 * If the spaceSort property is true, this method creates the spaceSortedX
		 * array.
		 *
		 * @param time The duration, in seconds, of the current frame.
		 */
		override protected function sortParticles():void
		{
			if( spaceSort )
			{
				_particles.sort( sortOnX );
				var len:int = _particles.length;
				for( var i:int = 0; i < len; ++i )
				{
					Particle3D( _particles[ i ] ).sortID = i;
				}
			}
		}

		private function sortOnX( p1:Particle3D, p2:Particle3D ):int
		{
			return p1.position.x - p2.position.x;
		}
	}
}

Fix sort error in Emitter3D

Fix sort error in Emitter3D


// [ ] Variable declaration
let name: type = expression;

// [ ] Basic types (arrow)
/*

byte (8-bit, unsigned)
bool (1-bit[*])

int8
int16
int32
int64
int128

uint8
uint16
uint32
uint64
uint128

intptr (size of a pointer, signed)
uintptr (size of a pointer, unsigned)

char (32-bit, unsigned)

float16
float32
float64
float128

*/

// [ ] Expressions
// [ ]    - Add
// [ ]    - Subtract
// [ ]    - Multiply
// [ ]    - Divide
// [ ]    - Modulo
// [ ]    - Logical And
// [ ]    - Logical Or
// [ ]    - Logical Not
// [ ]    - Relational GT
// [ ]    - Relational GE
// [ ]    - Relational LT
// [ ]    - Relational LE
// [ ]    - Relational And
// [ ]    - Relational Or
// [ ]    - Bitwise And
// [ ]    - Bitwise Or
// [ ]    - Bitwise Xor
// [ ]    - Bitwise Not

// [ ] Pointers
// [ ]    - Type
// [ ]    - Address Of
// [ ]    - Dereference

// [ ] Cast

// [ ] Function declaration
def main() {
}

// [ ] Extern function declaration
extern def puts(s: *byte);

// [ ] Extern function declaration w/ABI
extern "stdcall" def CreateWindowExA();

// [ ] Function call (extern AND local)
main();
puts("Hello");

// [ ] Extern import
extern import "stdint.h";

// [ ] Module (namespace)
module cstdint { extern import "stdint.h"; }

// [ ] c module (built-in)
import "c";

/*

// [ ] Basic types (in C)
c.char
c.uchar
c.schar
c.int
c.uint
c.short
c.ushort
c.long
c.ulong
c.float
c.double
c.ldouble

*/

// [ ] BigInt
int  // (∞-bit, signed)

Add a roadmap (of sorts)

Add a roadmap (of sorts)


package com.threerings.util {

import com.threerings.util.Equalable;

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

public class Name extends Object
    implements Comparable, Hashable, Streamable
{
    public function Name (name :String = "")
    {
        _name = name;
    }

    public function getNormal () :String
    {
        if (_normal == null) {
            _normal = normalize(_name);
            if (_normal === _name) {
                _normal = _name;
            }
        }
        return _normal;
    }

    public function isValid () :Boolean
    {
        return !isBlank();
    }

    public function isBlank () :Boolean
    {
        return Name.isBlank(this);
    }

    public function toString () :String
    {
        return _name;
    }

    // from interface Hashable
    public function equals (other :Object) :Boolean
    {
        return (ClassUtil.getClass(other) == Name) &&
            (getNormal() === (other as Name).getNormal());
    }

    // from interface Hashable
    public function hashCode () :int
    {
        return StringUtil.hashCode(getNormal());
    }

    // from interface Comparable
    public function compareTo (other :Object) :int
    {
        var thisNormal :String = getNormal();
        var thatNormal :String = (other as Name).getNormal();
        if (thisNormal == thatNormal) {
            return 0;

        } if (thisNormal < thatNormal) {
            return -1;

        }  else {
            return 1;
        }
    }

    // from interface Streamable
    public function writeObject (out :ObjectOutputStream) :void
        //throws IOError
    {
        out.writeField(_name);
    }

    // from interface Streamable
    public function readObject (ins :ObjectInputStream) :void
        //throws IOError
    {
        _name = (ins.readField(String) as String);
    }

    protected function normalize (txt :String) :String
    {
        return txt.toLowerCase();
    }

    public static function isBlank (name :Name) :Boolean
    {
        return (name == null || "" === name.toString());
    }

    /** The raw name text. */
    protected var _name :String;

    /** The normalized name text. */
    protected var _normal :String;
}
}

package com.threerings.util {

import com.threerings.util.Equalable;

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

public class Name extends Object
    implements Comparable, Hashable, Streamable
{
    public function Name (name :String = "")
    {
        _name = name;
    }

    public function getNormal () :String
    {
        if (_normal == null) {
            _normal = normalize(_name);
            if (_normal === _name) {
                _normal = _name;
            }
        }
        return _normal;
    }

    public function isValid () :Boolean
    {
        return !isBlank();
    }

    public function isBlank () :Boolean
    {
        return Name.isBlank(this);
    }

    public function toString () :String
    {
        return _name;
    }

    // from interface Hashable
    public function equals (other :Object) :Boolean
    {
        return (ClassUtil.getClass(other) == Name) &&
            (getNormal() === (other as Name).getNormal());
    }

    // from interface Hashable
    public function hashCode () :int
    {
        return StringUtil.hashCode(getNormal());
    }

    // from interface Comparable
    public function compareTo (other :Object) :int
    {
        var thisNormal :String = getNormal();
        var thatNormal :String = (other as Name).getNormal();
        return thisNormal.localeCompare(thatNormal);
    }

    // from interface Streamable
    public function writeObject (out :ObjectOutputStream) :void
        //throws IOError
    {
        out.writeField(_name);
    }

    // from interface Streamable
    public function readObject (ins :ObjectInputStream) :void
        //throws IOError
    {
        _name = (ins.readField(String) as String);
    }

    protected function normalize (txt :String) :String
    {
        return txt.toLowerCase();
    }

    public static function isBlank (name :Name) :Boolean
    {
        return (name == null || "" === name.toString());
    }

    /** The raw name text. */
    protected var _name :String;

    /** The normalized name text. */
    protected var _normal :String;
}
}

Use localeCompare.

Use localeCompare.

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