<commit_before>package com.axis.rtspclient {
  import com.axis.ClientEvent;
  import com.axis.NetStreamClient;
  import com.axis.ErrorManager;
  import com.axis.http.auth;
  import com.axis.http.request;
  import com.axis.http.url;
  import com.axis.IClient;
  import com.axis.Logger;
  import com.axis.rtspclient.FLVMux;
  import com.axis.rtspclient.RTP;
  import com.axis.rtspclient.SDP;

  import flash.events.AsyncErrorEvent;
  import flash.events.Event;
  import flash.events.EventDispatcher;
  import flash.events.IOErrorEvent;
  import flash.events.NetStatusEvent;
  import flash.events.SecurityErrorEvent;
  import flash.media.Video;
  import flash.net.NetConnection;
  import flash.net.NetStream;
  import flash.net.Socket;
  import flash.utils.ByteArray;
  import flash.events.TimerEvent;
  import flash.utils.Timer;

  import mx.utils.StringUtil;

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

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

    private var state:int = STATE_INITIAL;
    private var handle:IRTSPHandle;

    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 prevMethod:Function;

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

    private var bcTimer:Timer;
    private var connectionBroken:Boolean = false;

    private var nc:NetConnection = null;

    public function RTSPClient(urlParsed:Object, handle:IRTSPHandle) {
      this.userAgent = "Locomote " + StringUtil.trim(new Version().toString());
      this.state = STATE_INITIAL;
      this.handle = handle;
      this.urlParsed = urlParsed;
      this.bcTimer = new Timer(Player.config.connectionTimeout * 1000, 1);
      this.bcTimer.addEventListener(TimerEvent.TIMER_COMPLETE, bcTimerHandler);
      this.bcTimer.stop(); // Don't start timeout immediately

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

    public function start():Boolean {
      var self:RTSPClient = this;
      handle.addEventListener('connected', function():void {
        if (state !== STATE_INITIAL) {
          ErrorManager.dispatchError(805);
          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();
        }
      });

      nc = new NetConnection();
      nc.connect(null);
      nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
      nc.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
      nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatusError);
      nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
      this.ns = new NetStream(nc);
      this.setupNetStream();

      handle.connect();
      return true;
    }

    public function pause():Boolean {
      if (state !== STATE_PLAYING) {
        return false;
      }

      sendPauseReq();
      return true;
    }

    public function resume():Boolean {
      if (state !== STATE_PAUSED) {
        ErrorManager.dispatchError(801);
        return false;
      }

      /* Start time here so we can get a connection broken if the socket has gone away */
      bcTimer.reset();
      bcTimer.start();

      sendPlayReq();
      return true;
    }

    public function stop():Boolean {
      sendTeardownReq();
      nc.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
      nc.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);
      nc.removeEventListener(NetStatusEvent.NET_STATUS, onNetStatusError);
      nc.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
      return true;
    }

    public function seek(position:Number):Boolean {
      return false;
    }

    public function setBuffer(seconds:Number):Boolean {
      this.ns.bufferTime = seconds;
      this.ns.close();
      dispatchEvent(new ClientEvent(ClientEvent.PAUSED, { 'reason': 'buffering' }));
      this.ns.play(null);
      return true;
    }

    private function onClose(event:Event):void {
      if (state === STATE_TEARDOWN) {
        this.ns.dispose();
        dispatchEvent(new ClientEvent(ClientEvent.STOPPED));
      } else {
        if (!connectionBroken)
          ErrorManager.dispatchError(803);
      }
    }

    private function onData(event:Event):void {
      if (state === STATE_PLAYING) {
        bcTimer.reset();
        bcTimer.start();
        connectionBroken = false;
      }

      if (0 < data.bytesAvailable) {
        /* Determining byte have already been read. This is a continuation */
      } else {
        /* Read the determining byte */
        handle.readBytes(data, data.position, 1);
      }

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

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

        default:
          ErrorManager.dispatchError(804, [data[0].toString(16)]);
          stop();
          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'];

        if (authOpts.stale && authOpts.stale.toUpperCase() === 'TRUE') {
          requestReset();
          prevMethod();
          return false;
        }

        var newAuthState:String = auth.nextMethod(authState, authOpts);
        if (authState === newAuthState) {
          ErrorManager.dispatchError(parsed.code);
          return false;
        }

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

      if (isNaN(parsed.code)) {
        ErrorManager.dispatchError(parsed.code);
        return false;
      }

      if (parsed.headers['content-length']) {
        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']);
        Logger.log('RTSP IN:', oBody.toString());
      } else {
        Logger.log('RTSP IN:', data.toString());
      }

      requestReset();
      return parsed;
    }

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

      if (200 !== parsed.code) {
        ErrorManager.dispatchError(parsed.code);
        return;
      }

      switch (state) {
      case STATE_INITIAL:
        Logger.log("RTSPClient: STATE_INITIAL");

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

        break;
      case STATE_DESCRIBE:
        Logger.log("RTSPClient: STATE_DESCRIBE");

        if (!sdp.parse(body)) {
          ErrorManager.dispatchError(806);
          return;
        }

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

        if (0 === tracks.length) {
          ErrorManager.dispatchError(807);
          return;
        }

        /* Fall through, it's time for setup */
      case STATE_SETUP:
        Logger.log("RTSPClient: STATE_SETUP");
        Logger.log(parsed.headers['transport']);

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

        if (state === STATE_SETUP) {
          /* this is not the case when falling through, e.g. SETUP of first track */
          if (!(/^RTP\/AVP\/TCP;/.test(parsed.headers["transport"]) &&
	    /unicast/.test(parsed.headers["transport"]) &&
            /interleaved=/.test(parsed.headers["transport"]) )){
            dispatchEvent(new ClientEvent(ClientEvent.STOPPED));
            connectionBroken = true;
            handle.disconnect();
            ErrorManager.dispatchError(461);
            return;
          }
        }

        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:
        Logger.log("RTSPClient: STATE_PLAY");
        state = STATE_PLAYING;

        if (this.flvmux) {
          /* If the flvmux have been initialized don't do it again.<commit_msg>Revert "tabs replaced with doublespaces near new regexp"<commit_after>package com.axis.rtspclient {
  import com.axis.ClientEvent;
  import com.axis.NetStreamClient;
  import com.axis.ErrorManager;
  import com.axis.http.auth;
  import com.axis.http.request;
  import com.axis.http.url;
  import com.axis.IClient;
  import com.axis.Logger;
  import com.axis.rtspclient.FLVMux;
  import com.axis.rtspclient.RTP;
  import com.axis.rtspclient.SDP;

  import flash.events.AsyncErrorEvent;
  import flash.events.Event;
  import flash.events.EventDispatcher;
  import flash.events.IOErrorEvent;
  import flash.events.NetStatusEvent;
  import flash.events.SecurityErrorEvent;
  import flash.media.Video;
  import flash.net.NetConnection;
  import flash.net.NetStream;
  import flash.net.Socket;
  import flash.utils.ByteArray;
  import flash.events.TimerEvent;
  import flash.utils.Timer;

  import mx.utils.StringUtil;

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

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

    private var state:int = STATE_INITIAL;
    private var handle:IRTSPHandle;

    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 prevMethod:Function;

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

    private var bcTimer:Timer;
    private var connectionBroken:Boolean = false;

    private var nc:NetConnection = null;

    public function RTSPClient(urlParsed:Object, handle:IRTSPHandle) {
      this.userAgent = "Locomote " + StringUtil.trim(new Version().toString());
      this.state = STATE_INITIAL;
      this.handle = handle;
      this.urlParsed = urlParsed;
      this.bcTimer = new Timer(Player.config.connectionTimeout * 1000, 1);
      this.bcTimer.addEventListener(TimerEvent.TIMER_COMPLETE, bcTimerHandler);
      this.bcTimer.stop(); // Don't start timeout immediately

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

    public function start():Boolean {
      var self:RTSPClient = this;
      handle.addEventListener('connected', function():void {
        if (state !== STATE_INITIAL) {
          ErrorManager.dispatchError(805);
          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();
        }
      });

      nc = new NetConnection();
      nc.connect(null);
      nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
      nc.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
      nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatusError);
      nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
      this.ns = new NetStream(nc);
      this.setupNetStream();

      handle.connect();
      return true;
    }

    public function pause():Boolean {
      if (state !== STATE_PLAYING) {
        return false;
      }

      sendPauseReq();
      return true;
    }

    public function resume():Boolean {
      if (state !== STATE_PAUSED) {
        ErrorManager.dispatchError(801);
        return false;
      }

      /* Start time here so we can get a connection broken if the socket has gone away */
      bcTimer.reset();
      bcTimer.start();

      sendPlayReq();
      return true;
    }

    public function stop():Boolean {
      sendTeardownReq();
      nc.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
      nc.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);
      nc.removeEventListener(NetStatusEvent.NET_STATUS, onNetStatusError);
      nc.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
      return true;
    }

    public function seek(position:Number):Boolean {
      return false;
    }

    public function setBuffer(seconds:Number):Boolean {
      this.ns.bufferTime = seconds;
      this.ns.close();
      dispatchEvent(new ClientEvent(ClientEvent.PAUSED, { 'reason': 'buffering' }));
      this.ns.play(null);
      return true;
    }

    private function onClose(event:Event):void {
      if (state === STATE_TEARDOWN) {
        this.ns.dispose();
        dispatchEvent(new ClientEvent(ClientEvent.STOPPED));
      } else {
        if (!connectionBroken)
          ErrorManager.dispatchError(803);
      }
    }

    private function onData(event:Event):void {
      if (state === STATE_PLAYING) {
        bcTimer.reset();
        bcTimer.start();
        connectionBroken = false;
      }

      if (0 < data.bytesAvailable) {
        /* Determining byte have already been read. This is a continuation */
      } else {
        /* Read the determining byte */
        handle.readBytes(data, data.position, 1);
      }

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

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

        default:
          ErrorManager.dispatchError(804, [data[0].toString(16)]);
          stop();
          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'];

        if (authOpts.stale && authOpts.stale.toUpperCase() === 'TRUE') {
          requestReset();
          prevMethod();
          return false;
        }

        var newAuthState:String = auth.nextMethod(authState, authOpts);
        if (authState === newAuthState) {
          ErrorManager.dispatchError(parsed.code);
          return false;
        }

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

      if (isNaN(parsed.code)) {
        ErrorManager.dispatchError(parsed.code);
        return false;
      }

      if (parsed.headers['content-length']) {
        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']);
        Logger.log('RTSP IN:', oBody.toString());
      } else {
        Logger.log('RTSP IN:', data.toString());
      }

      requestReset();
      return parsed;
    }

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

      if (200 !== parsed.code) {
        ErrorManager.dispatchError(parsed.code);
        return;
      }

      switch (state) {
      case STATE_INITIAL:
        Logger.log("RTSPClient: STATE_INITIAL");

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

        break;
      case STATE_DESCRIBE:
        Logger.log("RTSPClient: STATE_DESCRIBE");

        if (!sdp.parse(body)) {
          ErrorManager.dispatchError(806);
          return;
        }

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

        if (0 === tracks.length) {
          ErrorManager.dispatchError(807);
          return;
        }

        /* Fall through, it's time for setup */
      case STATE_SETUP:
        Logger.log("RTSPClient: STATE_SETUP");
        Logger.log(parsed.headers['transport']);

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

        if (state === STATE_SETUP) {
          /* this is not the case when falling through, e.g. SETUP of first track */
          if (!(/^RTP\/AVP\/TCP;/.test(parsed.headers["transport"]) &&
        	/unicast/.test(parsed.headers["transport"]) &&
        	/interleaved=/.test(parsed.headers["transport"]) )){
            dispatchEvent(new ClientEvent(ClientEvent.STOPPED));
            connectionBroken = true;
            handle.disconnect();
            ErrorManager.dispatchError(461);
            return;
          }
        }

        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:
        Logger.log("RTSPClient: STATE_PLAY");
        state = STATE_PLAYING;

        if (this.flvmux) {
          /* If the flvmux have been initialized don't do it again.
{'fraction_non_alphanumeric': 0.11557013312753232, 'fraction_numerical': 0.007042253521126761, 'mean_word_length': 2.4184666117065126, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4316809', 'n_tokens_mistral': 6149, 'n_tokens_neox': 5852, 'n_words': 1378}