RTSP Client Connected to VLC

Asked by At

I am attempting to work on a larger project which involves creating an RTSP server and client pair in order to stream music. Right now, I am only working on seeing how to implement a RTSP client and using VLC as a server so that I can check that I understand this portion of the problem.

(Please note, I did not write the code for this client completely. I found it from an online resource and changed it slightly for testing purposes. For my project I plan on using this client only as a basis for inspiration)

On VLC, here is the output stream code generated when I use the GUI to set up the stream

:sout=#transcode{vcodec=h264,vb=0,scale=0,acodec=mp4a,ab=128,channels=2,samplerate=44100}:rtp{sdp=rtsp://:5544/stream} :no-sout-rtp-sap :no-sout-standard-sap :sout-all :ttl=1 :sout-keep

Please note that I was able to successfully receive this stream on another instance of VLC media player, so I am at least marginally sure that it is transmitting the stream correctly, at least on the local machine I am running it on.

Here is the code for my java Client

/*Client.java*/

/* ------------------
   Client
   usage: java Client [Server hostname] [Server RTSP listening port] [Video file requested]
   ---------------------- */

import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.Timer;

public class Client{

  //GUI
  //----
  JFrame f = new JFrame("Client");
  JButton setupButton = new JButton("Setup");
  JButton playButton = new JButton("Play");
  JButton pauseButton = new JButton("Pause");
  JButton tearButton = new JButton("Teardown");
  JPanel mainPanel = new JPanel();
  JPanel buttonPanel = new JPanel();
  JLabel iconLabel = new JLabel();
  ImageIcon icon;


  //RTP variables:
  //----------------
  DatagramPacket rcvdp; //UDP packet received from the server
  DatagramSocket RTPsocket; //socket to be used to send and receive UDP packets
  static int RTP_RCV_PORT = 5544; //port where the client will receive the RTP packets

  Timer timer; //timer used to receive data from the UDP socket
  byte[] buf; //buffer used to store data received from the server 

  //RTSP variables
  //----------------
  //rtsp states 
  final static int INIT = 0;
  final static int READY = 1;
  final static int PLAYING = 2;
  static int state; //RTSP state == INIT or READY or PLAYING
  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 to request to the server
  int RTSPSeqNb = 0; //Sequence number of RTSP messages within the session
  int RTSPid = 0; //ID of the RTSP session (given by the RTSP Server)

  final static String CRLF = "\r\n";

  //Video constants:
  //------------------
  static int MJPEG_TYPE = 26; //RTP payload type for MJPEG video

  //--------------------------
  //Constructor
  //--------------------------
  public Client() {

    //build GUI
    //--------------------------

    //Frame
    f.addWindowListener(new WindowAdapter() {
       public void windowClosing(WindowEvent e) {
         System.exit(0);
       }
    });

    //Buttons
    buttonPanel.setLayout(new GridLayout(1,0));
    buttonPanel.add(setupButton);
    buttonPanel.add(playButton);
    buttonPanel.add(pauseButton);
    buttonPanel.add(tearButton);
    setupButton.addActionListener(new setupButtonListener());
    playButton.addActionListener(new playButtonListener());
    pauseButton.addActionListener(new pauseButtonListener());
    tearButton.addActionListener(new tearButtonListener());

    //Image display label
    iconLabel.setIcon(null);

    //frame layout
    mainPanel.setLayout(null);
    mainPanel.add(iconLabel);
    mainPanel.add(buttonPanel);
    iconLabel.setBounds(0,0,380,280);
    buttonPanel.setBounds(0,280,380,50);

    f.getContentPane().add(mainPanel, BorderLayout.CENTER);
    f.setSize(new Dimension(390,370));
    f.setVisible(true);

    //init timer
    //--------------------------
    timer = new Timer(20, new timerListener());
    timer.setInitialDelay(0);
    timer.setCoalesce(true);

    //allocate enough memory for the buffer used to receive data from the server
    buf = new byte[15000];    
  }

  //------------------------------------
  //main
  //------------------------------------
  public static void main(String argv[]) throws Exception
  {
    //Create a Client object
    Client theClient = new Client();

    //get server RTSP port and IP address from the command line
    //------------------
    int RTSP_server_port = 5544;
    String ServerHost = "localhost";
    InetAddress ServerIPAddr = InetAddress.getByName(ServerHost);

    //get video filename to request:
    VideoFileName = "M13_Assignment.mp4";

    //Establish a TCP connection with the server to exchange RTSP messages
    //------------------
    theClient.RTSPsocket = new Socket(ServerIPAddr, RTSP_server_port);

    //Set input and output stream filters:
    RTSPBufferedReader = new BufferedReader(new InputStreamReader(theClient.RTSPsocket.getInputStream()) );
    RTSPBufferedWriter = new BufferedWriter(new OutputStreamWriter(theClient.RTSPsocket.getOutputStream()) );

    //init RTSP state:
    state = INIT;
  }


  //------------------------------------
  //Handler for buttons
  //------------------------------------

  //.............
  //TO COMPLETE
  //.............

  //Handler for Setup button
  //-----------------------
  class setupButtonListener implements ActionListener{
    public void actionPerformed(ActionEvent e){

      //System.out.println("Setup Button pressed !");      

      if (state == INIT) 
        {
          try{
              //Init non-blocking RTPsocket that will be used to receive data
              try{
                  //construct a new DatagramSocket to receive RTP packets from the server, on port RTP_RCV_PORT
                  RTPsocket = new DatagramSocket(RTP_RCV_PORT);

                  //set TimeOut value of the socket to 5msec.
                  RTPsocket.setSoTimeout(5);

              }
              catch (SocketException se)
              {
                  System.out.println("Socket exception: "+se);
                  System.exit(0);
              }

              //init RTSP sequence number
              RTSPSeqNb = 1;

              //Send SETUP message to the server
              send_RTSP_request("SETUP");

              //Wait for the response
              if (parse_server_response() != 200)
                  System.out.println("Invalid Server Response");
              else
              {
                  //change RTSP state and print new state
                  state = READY;
                  //System.out.println("New RTSP state: ....");
              }
          }//else if state != INIT then do nothing

          catch (Exception ex)
            {
              Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
  }

  //Handler for Play button
  //-----------------------
  class playButtonListener implements ActionListener {
    public void actionPerformed(ActionEvent e){

      //System.out.println("Play Button pressed !"); 

      if (state == READY) 
        {
          try {
              //increase RTSP sequence number
              RTSPSeqNb = RTSPSeqNb++;


              //Send PLAY message to the server
              send_RTSP_request("PLAY");

              //Wait for the response
              if (parse_server_response() != 200)
                  System.out.println("Invalid Server Response");
              else
              {
                  //change RTSP state and print out new state
                  state=PLAYING;
                  // System.out.println("New RTSP state: ...")

                  //start the timer
                  timer.start();
              }
          } //else if state != READY then do nothing
          catch (Exception ex) {
              Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
          }
        }
    }
  }


  //Handler for Pause button
  //-----------------------
  class pauseButtonListener implements ActionListener {
    public void actionPerformed(ActionEvent e){

      //System.out.println("Pause Button pressed !");   

      if (state == PLAYING) 
        {
          try {
              //increase RTSP sequence number
              RTSPSeqNb = RTSPSeqNb++;

              //Send PAUSE message to the server
              send_RTSP_request("PAUSE");

              //Wait for the response
              if (parse_server_response() != 200)
                  System.out.println("Invalid Server Response");
              else
              {
                  //change RTSP state and print out new state
                  state= READY;
                  //System.out.println("New RTSP state: ...");

                  //stop the timer
                  timer.stop();
              }
          }
          //else if state != PLAYING then do nothing
          catch (Exception ex) {
              Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
          }
        }
    }
  }

  //Handler for Teardown button
  //-----------------------
  class tearButtonListener implements ActionListener {
    public void actionPerformed(ActionEvent e){

      //System.out.println("Teardown Button pressed !");  

      //increase RTSP sequence number
      RTSPSeqNb = RTSPSeqNb++;


      //Send TEARDOWN message to the server
      send_RTSP_request("TEARDOWN");

        try {
            //Wait for the response
            if (parse_server_response() != 200)
                System.out.println("Invalid Server Response");
            else
            {
                //change RTSP state and print out new state
                state=INIT;
                //System.out.println("New RTSP state: ...");

                //stop the timer
                timer.stop();

                //exit
                System.exit(0);
            }
        } catch (Exception ex) {
            Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
  }


  //------------------------------------
  //Handler for timer
  //------------------------------------

  class timerListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {

      //Construct a DatagramPacket to receive data from the UDP socket
      rcvdp = new DatagramPacket(buf, buf.length);

      try{
        //receive the DP from the socket:
        RTPsocket.receive(rcvdp);

        //create an RTPpacket object from the DP
        RTPpacket rtp_packet = new RTPpacket(rcvdp.getData(), rcvdp.getLength());

        //print important header fields of the RTP packet received: 
        System.out.println("Got RTP packet with SeqNum # "+rtp_packet.getsequencenumber()+" TimeStamp "+rtp_packet.gettimestamp()+" ms, of type "+rtp_packet.getpayloadtype());

        //print header bitstream:
        rtp_packet.printheader();

        //get the payload bitstream from the RTPpacket object
        int payload_length = rtp_packet.getpayload_length();
        byte [] payload = new byte[payload_length];
        rtp_packet.getpayload(payload);

        //get an Image object from the payload bitstream
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Image image = toolkit.createImage(payload, 0, payload_length);

        //display the image as an ImageIcon object
        icon = new ImageIcon(image);
        iconLabel.setIcon(icon);
      }
      catch (InterruptedIOException iioe){
        //System.out.println("Nothing to read");
      }
      catch (IOException ioe) {
        System.out.println("Exception caught: "+ioe);
      }
    }
  }

  //------------------------------------
  //Parse Server Response
  //------------------------------------
  private int parse_server_response() throws Exception 
  {
    int reply_code = 0;

    try{
      //parse status line and extract the reply_code:
      String StatusLine = RTSPBufferedReader.readLine();
      //System.out.println("RTSP Client - Received from Server:");
      System.out.println(StatusLine);

      StringTokenizer tokens = new StringTokenizer(StatusLine);
      tokens.nextToken(); //skip over the RTSP version
      reply_code = Integer.parseInt(tokens.nextToken());

      //if reply code is OK get and print the 2 other lines
      if (reply_code == 200)
        {
          String SeqNumLine = RTSPBufferedReader.readLine();
          System.out.println(SeqNumLine);

          String SessionLine = RTSPBufferedReader.readLine();
          System.out.println(SessionLine);

          //if state == INIT gets the Session Id from the SessionLine
          tokens = new StringTokenizer(SessionLine);
          tokens.nextToken(); //skip over the Session:
          RTSPid = Integer.parseInt(tokens.nextToken());
        }
    }
    catch(Exception ex)
      {
        System.out.println("Exception caught c: "+ex);
        System.exit(0);
      }

    return(reply_code);
  }

  //------------------------------------
  //Send RTSP Request
  //------------------------------------

  //.............
  //TO COMPLETE
  //.............

  private void send_RTSP_request(String request_type)
  {
    try{
      //Use the RTSPBufferedWriter to write to the RTSP socket

      //write the request line:
      RTSPBufferedWriter.write(request_type+" "+VideoFileName+" RTSP/1.0"+CRLF);
      System.out.print(request_type+" "+VideoFileName+" RTSP/1.0"+CRLF);

      //write the CSeq line: 
      RTSPBufferedWriter.write("CSeq "+ RTSPSeqNb +CRLF);
      System.out.print("CSeq "+ RTSPSeqNb +CRLF);

      //check if request_type is equal to "SETUP" and in this case write the Transport: line advertising to the server the port used to receive the RTP packets RTP_RCV_PORT
      if(request_type.equals("SETUP"))
      {
          RTSPBufferedWriter.write("Transport: RTP/UDP; client_port= "+ RTP_RCV_PORT + CRLF);
          System.out.print("Transport: RTP/UDP; client_port= "+ RTP_RCV_PORT + CRLF);
      }
      //otherwise, write the Session line from the RTSPid field
      else
      {
          RTSPBufferedWriter.write("Session: "+ RTSPid + CRLF);
          System.out.print("Session: "+ RTSPid + CRLF);
      }
      try
      {
      RTSPBufferedWriter.flush();
      }
      catch(IOException e)
      {
          System.out.println("Exception caught a: "+e.toString());
      }
    }
    catch(Exception ex)
      {
        System.out.println("Exception caught b: "+ex);
        System.exit(0);
      }
  }

}//end of Class Client

The client will connect to the VLC output stream. However, once I tried to press the "setup" button it will freeze. I have debugged/traced the java program with netbeans and I've found that once the program gets to the parseserverresponse() method it will hang up. Is this caused by VLC or is there a more fundamental mistake I am making?

0 Answers