gt:                 ios = ImageIO.createImageOutputStream(baos);
context: package com.chuidiang.rtsp;/* ------------------
     com.chuidiang.rtsp.Server
     usage: java com.chuidiang.rtsp.Server [RTSP listening port]
     ---------------------- */
  
  
  import javax.imageio.IIOImage;
  import javax.imageio.ImageIO;
  import javax.imageio.ImageWriteParam;
  import javax.imageio.ImageWriter;
  import javax.imageio.stream.ImageOutputStream;
  import javax.swing.*;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.image.BufferedImage;
  import java.io.*;
  import java.net.*;
  import java.util.Arrays;
  import java.util.Iterator;
  import java.util.StringTokenizer;
  import java.util.UUID;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  public class Server implements ActionListener {
  
      //RTP variables:
      //----------------
      DatagramSocket RTPsocket; //socket to be used to send and receive UDP packets
      DatagramPacket senddp; //UDP packet containing the video frames
  
      InetAddress ClientIPAddr;   //com.chuidiang.rtsp.Client IP address
      int RTP_dest_port = 0;      //destination port for RTP packets  (given by the RTSP com.chuidiang.rtsp.Client)
      int RTSP_dest_port = 0;
  
  
      //Video variables:
      //----------------
      int imagenb = 0; //image nb of the image currently transmitted
      VideoStream video; //com.chuidiang.rtsp.VideoStream object used to access video frames
      static int MJPEG_TYPE = 26; //RTP payload type for MJPEG video
      static int FRAME_PERIOD = 100; //Frame period of the video to stream, in ms
      static int VIDEO_LENGTH = 500; //length of the video in frames
  
      Timer timer;    //timer used to send the images at the video frame rate
      byte[] buf;     //buffer used to store the images to send to the client 
      int sendDelay;  //the delay to send images over the wire. Ideally should be
                      //equal to the frame rate of the video file, but may be 
                      //adjusted when congestion is detected.
  
      //RTSP variables
      //----------------
      //rtsp states
      final static int INIT = 0;
      final static int READY = 1;
      final static int PLAYING = 2;
      //rtsp message types
      final static int SETUP = 3;
      final static int PLAY = 4;
      final static int PAUSE = 5;
      final static int TEARDOWN = 6;
      final static int DESCRIBE = 7;
      final static int OPTIONS = 8;
  
      int state; //RTSP com.chuidiang.rtsp.Server state == INIT or READY or PLAY
      Socket RTSPsocket; //socket used to send/receive RTSP messages
      //input and output stream filters
      static BufferedReader RTSPBufferedReader;
      static BufferedWriter RTSPBufferedWriter;
      static String VideoFileName; //video file requested from the client
      static String RTSPid = UUID.randomUUID().toString(); //ID of the RTSP session
      int RTSPSeqNb = 0; //Sequence number of RTSP messages within the session
  
  
      //RTCP variables
      //----------------
      static int RTCP_RCV_PORT = 19001; //port where the client will receive the RTP packets
      static int RTCP_PERIOD = 400;     //How often to check for control events
      DatagramSocket RTCPsocket = null;
      RtcpReceiver rtcpReceiver;
      int congestionLevel;
  
      //Performance optimization and Congestion control
      ImageTranslator imgTranslator;
      CongestionController cc;
      
      final static String CRLF = "\r\n";
  
      //--------------------------------
      //Constructor
      //--------------------------------
      public Server(Socket client, int port) throws IOException {
          RTSPsocket = client;
          RTSP_dest_port = port;
          ClientIPAddr = client.getInetAddress();
          state = INIT;
  
          RTSPBufferedReader = new BufferedReader(new InputStreamReader(RTSPsocket.getInputStream()) );
          RTSPBufferedWriter = new BufferedWriter(new OutputStreamWriter(RTSPsocket.getOutputStream()) );
  
          //init RTP sending Timer
          sendDelay = FRAME_PERIOD;
          timer = new Timer(sendDelay, this);
          timer.setInitialDelay(0);
          timer.setCoalesce(true);
  
          //init congestion controller
          cc = new CongestionController(600);
  
          //allocate memory for the sending buffer
          buf = new byte[60000];
  
  
          //init the RTCP packet receiver
          rtcpReceiver = new RtcpReceiver(RTCP_PERIOD);
  
  
          //Video encoding and quality
          imgTranslator = new ImageTranslator(0.8f);
  
          new Thread(){
              public void run(){
                  try {
                      Server.this.run();
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          }.start();
      }
            
      //------------------------------------
      //main
      //------------------------------------
      public  void run() throws Exception
      {
          //create a com.chuidiang.rtsp.Server object
  //        Server server = new Server();
  
  
  
          //Initiate TCP connection with the client for the RTSP session
  
  //        listenSocket.close();
  
  
          //Set input and output stream filters:
  
          //Wait for the SETUP message from the client
          int request_type;
          boolean done = false;
          while(!done) {
              request_type = parseRequest(); //blocking
      
              if (request_type == SETUP) {
                  done = true;
  
                  //update RTSP state
                  state = READY;
                  System.out.println("New RTSP state: READY");
               
                  //Send response
                  sendResponse();
               
                  //init the com.chuidiang.rtsp.VideoStream object:
                  video = new VideoStream(VideoFileName);
  
                  //init RTP and RTCP sockets
                  RTPsocket = new DatagramSocket();
                  RTCPsocket = new DatagramSocket(RTCP_RCV_PORT);
              }
  
              if (request_type==OPTIONS){
                  sendOptions();
              }
          }
  
          //loop to handle RTSP requests
          while(true) {
              //parse the request
              request_type = parseRequest(); //blocking
                  
              if ((request_type == PLAY) && (state == READY)) {
                  //send back response
                  sendResponse();
                  //start timer
                  timer.start();
                  rtcpReceiver.startRcv();
                  //update state
                  state = PLAYING;
                  System.out.println("New RTSP state: PLAYING");
              }
              else if ((request_type == PAUSE) && (state == PLAYING)) {
                  //send back response
                  sendResponse();
                  //stop timer
                  timer.stop();
                  rtcpReceiver.stopRcv();
                  //update state
                  state = READY;
                  System.out.println("New RTSP state: READY");
              }
              else if (request_type == TEARDOWN) {
                  //send back response
                  sendResponse();
                  //stop timer
                  timer.stop();
                  rtcpReceiver.stopRcv();
                  //close sockets
                  RTSPsocket.close();
                  RTPsocket.close();
  
                  System.exit(0);
              }
              else if (request_type == DESCRIBE) {
                  System.out.println("Received DESCRIBE request");
                  sendDescribe();
              }
              else if (request_type == OPTIONS) {
                  System.out.println("Received OPTIONS request");
                  sendOptions();
              }
          }
      }
  
      //------------------------
      //Handler for timer
      //------------------------
      public void actionPerformed(ActionEvent e) {
          byte[] frame;
  
          System.out.println("va!!");
  
          //if the current image nb is less than the length of the video
          if (imagenb < VIDEO_LENGTH) {
              //update current imagenb
              imagenb++;
  
              try {
                  //get next frame to send from the video, as well as its size
                  int image_length = video.getnextframe(buf);
  
  
                  //adjust quality of the image if there is congestion detected
                  if (congestionLevel > 0) {
                      imgTranslator.setCompressionQuality(1.0f - congestionLevel * 0.2f);
                      frame = imgTranslator.compress(Arrays.copyOfRange(buf, 0, image_length));
                      image_length = frame.length;
                      System.arraycopy(frame, 0, buf, 0, image_length);
                  }
  
                  //Builds an com.chuidiang.rtsp.RTPpacket object containing the frame
                  RTPpacket rtp_packet = new RTPpacket(MJPEG_TYPE, imagenb, imagenb*FRAME_PERIOD, buf, image_length);
                  
                  //get to total length of the full rtp packet to send
                  int packet_length = rtp_packet.getlength();
  
                  //retrieve the packet bitstream and store it in an array of bytes
                  byte[] packet_bits = new byte[packet_length];
                  rtp_packet.getpacket(packet_bits);
  
                  //send the packet as a DatagramPacket over the UDP socket 
                  senddp = new DatagramPacket(packet_bits, packet_length, ClientIPAddr, RTP_dest_port);
                  RTPsocket.send(senddp);
  
                  System.out.println("Send frame #" + imagenb + ", Frame size: " + image_length + " (" + buf.length + ")");
                  //print the header bitstream
                  rtp_packet.printheader();
  
              }
              catch(Exception ex) {
                  ex.printStackTrace();
                  System.exit(0);
              }
          }
          else {
              //if we have reached the end of the video file, stop the timer
              timer.stop();
              rtcpReceiver.stopRcv();
          }
      }
  
      //------------------------
      //Controls RTP sending rate based on traffic
      //------------------------
      class CongestionController implements ActionListener {
          private Timer ccTimer;
          int interval;   //interval to check traffic stats
          int prevLevel;  //previously sampled congestion level
  
          public CongestionController(int interval) {
              this.interval = interval;
              ccTimer = new Timer(interval, this);
              ccTimer.start();
          }
  
          public void actionPerformed(ActionEvent e) {
  
              //adjust the send rate
              if (prevLevel != congestionLevel) {
                  sendDelay = FRAME_PERIOD + congestionLevel * (int)(FRAME_PERIOD * 0.1);
                  timer.setDelay(sendDelay);
                  prevLevel = congestionLevel;
                  System.out.println("Send delay changed to: " + sendDelay);
              }
          }
      }
  
      //------------------------
      //Listener for RTCP packets sent from client
      //------------------------
      class RtcpReceiver implements ActionListener {
          private Timer rtcpTimer;
          private byte[] rtcpBuf;
          int interval;
  
          public RtcpReceiver(int interval) {
              //set timer with interval for receiving packets
              this.interval = interval;
              rtcpTimer = new Timer(interval, this);
              rtcpTimer.setInitialDelay(0);
              rtcpTimer.setCoalesce(true);
  
              //allocate buffer for receiving RTCP packets
              rtcpBuf = new byte[512];
          }
  
          public void actionPerformed(ActionEvent e) {
              //Construct a DatagramPacket to receive data from the UDP socket
              DatagramPacket dp = new DatagramPacket(rtcpBuf, rtcpBuf.length);
              float fractionLost;
  
              try {
                  RTCPsocket.receive(dp);   // Blocking
                  RTCPpacket rtcpPkt = new RTCPpacket(dp.getData(), dp.getLength());
                  System.out.println("[RTCP] " + rtcpPkt);
  
                  //set congestion level between 0 to 4
                  fractionLost = rtcpPkt.fractionLost;
                  if (fractionLost >= 0 && fractionLost <= 0.01) {
                      congestionLevel = 0;    //less than 0.01 assume negligible
                  }
                  else if (fractionLost > 0.01 && fractionLost <= 0.25) {
                      congestionLevel = 1;
                  }
                  else if (fractionLost > 0.25 && fractionLost <= 0.5) {
                      congestionLevel = 2;
                  }
                  else if (fractionLost > 0.5 && fractionLost <= 0.75) {
                      congestionLevel = 3;
                  }
                  else {
                      congestionLevel = 4;
                  }
              }
              catch (InterruptedIOException iioe) {
                  System.out.println("Nothing to read");
              }
              catch (IOException ioe) {
                  ioe.printStackTrace();
              }
          }
  
          public void startRcv() {
              rtcpTimer.start();
          }
  
          public void stopRcv() {
              rtcpTimer.stop();
          }
      }
  
      //------------------------------------
      //Translate an image to different encoding or quality
      //------------------------------------
      class ImageTranslator {
  
          private float compressionQuality;
          private ByteArrayOutputStream baos;
          private BufferedImage image;
          private Iterator<ImageWriter>writers;
          private ImageWriter writer;
          private ImageWriteParam param;
          private ImageOutputStream ios;
  
          public ImageTranslator(float cq) {
              compressionQuality = cq;
  
              try {
                  baos =  new ByteArrayOutputStream();
  