织梦CMS - 轻松建站从此开始!

罗索

Java ME下的流媒体开发体验(二):RTSP处理

落鹤生 发布于 2011-10-23 19:36 点击:次 
Recall that RTSP is the actual protocol over which streaming commands are initiated, through which the RTP packets are received. The RTSP protocol is like a command initiator, a bit like HTTP.
TAG:

本文相关的完整源码下载:http://bbs.rosoo.net/forum.php?mod=viewthread&tid=8535

Recall that RTSP is the actual protocol over which streaming commands are initiated, through which the RTP packets are received. The RTSP protocol is like a command initiator, a bit like HTTP. For a really good explanation of a typical RTSP session, please see these specifications for a simple RTSP client. For the purposes of this article, I am going to oversimplify the protocol implementation. Figure 1 shows the typical RTSP session between a client and a streaming server.

Figure 1 - A typical RTSP session
between a RTSP client and a streaming server
Figure 1. A typical RTSP session between a RTSP client and a streaming server (click for full-size image).

In a nutshell, an RTSP client initiates a session by sending a DESCRIBE request to the streaming server which means that the client wants more information about a media file. An example DESCRIBE request may look like this:

DESCRIBE rtsp://localhost:554/media.3gp rtsp/1.0 CSeq: 1

The URL for the media file is followed by the RTSP version that the client is following, and a carriage return/line feed (CRLF). The next line contains the sequence number of this request and increments for each subsequent request sent to the server. The command is terminated by a single line on its own (as are all RTSP commands).

All client commands that are successful receive a response that starts with RTSP/1.0 200 OK. For the DESCRIBE request, the server responds with several parameters, and if the file is present and streamable, this response contains any information for any tracks in special control strings that start with a a=control:trackID= String. The trackID is important and is used to create the next requests to the server.

Once described, the media file's separate tracks are set up for streaming using the SETUP command, and these commands should indicate the transport properties for the subsequent RTP packets. This is shown here:

SETUP rtsp://localhost:554/media.3gp/trackID=3 rtsp/1.0 CSeq: 2 TRANSPORT: UDP;unicast;client_port=8080-8081

The previous command indicates to the server to set up to stream trackID 3 of the media.3gp file, to send the packets via UDP, and to send them to port 8080 on the client (8081 is for RTCP commands). The response to the first SETUP command (if it is okay) will contain the session information for subsequent commands and must be included as shown here:

SETUP rtsp://localhost:554/media.3gp/trackID=3 rtsp/1.0
CSeq: 3
Session: 556372992204
TRANSPORT: UDP;unicast;client_port=8080-8081

An OK response from the server indicates that you can send the PLAY command, which will make the server start sending the RTP packets:

PLAY rtsp://localhost:554/media.3gp rtsp/1.0 CSeq: 3 Session: 556372992204

Notice that the PLAY command is issued only on the main media file, and not on any individual tracks. The same is true for the PAUSE and TEARDOWN commands, which are identical to the PLAY command, except for the command itself.

The following listing contains the RTSPProtocolHandler class. The comments in the code and the brief information so far should help with understanding how this protocol handler works:

  1. import java.util.Vector; 
  2. import java.io.InputStream; 
  3. import java.io.IOException; 
  4. import java.io.OutputStream; 
  5.  
  6. public class RTSPProtocolHandler { 
  7.     // the address of the media file as an rtsp://... String 
  8.     private String address; 
  9.  
  10.     // the inputstream to receive response from the server 
  11.     private InputStream is; 
  12.  
  13.     // the outputstream to write to the server 
  14.     private OutputStream os; 
  15.  
  16.     // the incrementing sequence number for each request 
  17.     // sent by the client 
  18.     private static int CSeq = 1; 
  19.  
  20.     // the session id sent by the server after an initial setup 
  21.     private String sessionId; 
  22.  
  23.     // the number of tracks in a media file 
  24.     private Vector tracks = new Vector(2); 
  25.  
  26.     // flags to indicate the status of a session 
  27.     private boolean described, setup, playing; 
  28.     private Boolean stopped = true
  29.  
  30.     // constants 
  31.     private static final String CRLF = " "
  32.     private static final String VERSION = "rtsp/1.0"
  33.     private static final String TRACK_LINE = "a=control:trackID="
  34.     private static final String TRANSPORT_DATA = 
  35.       "TRANSPORT: UDP;unicast;client_port=8080-8081"
  36.     private static final String RTSP_OK = "RTSP/1.0 200 OK"
  37.  
  38.     // base constructor, takes the media address, input and output streams 
  39.     public RTSPProtocolHandler( 
  40.         String address, InputStream is, OutputStream Os) { 
  41.  
  42.         this.address = address; 
  43.         this.is = is; 
  44.         this.os = Os; 
  45.     } 
  46.  
  47.     // creates, sends and parses a DESCRIBE client request 
  48.     public void doDescribe() throws IOException { 
  49.  
  50.         // if already described, return 
  51.         if(described) return
  52.  
  53.         // create the base command 
  54.         String baseCommand = getBaseCommand("DESCRIBE " + address); 
  55.  
  56.         // execute it and read the response 
  57.         String response = doCommand(baseCommand); 
  58.  
  59.         // the response will contain track information, amongst other things 
  60.         parseTrackInformation(response); 
  61.  
  62.         // set flag 
  63.         described = true
  64.     } 
  65.  
  66.     // creates, sends and parses a SETUP client request 
  67.     public void doSetup() throws IOException { 
  68.  
  69.         // if not described 
  70.         if(!described) throw new IOException("Not Described!"); 
  71.  
  72.         // create the base command for the first SETUP track 
  73.         String baseCommand = 
  74.           getBaseCommand( 
  75.                 "SETUP " + address + "/trackID=" + tracks.elementAt(0)); 
  76.  
  77.         // add the static transport data 
  78.         baseCommand += CRLF + TRANSPORT_DATA; 
  79.  
  80.         // read response 
  81.         String response = doCommand(baseCommand); 
  82.  
  83.         // parse it for session information 
  84.         parseSessionInfo(response); 
  85.  
  86.         // if session information cannot be parsed, it is an error 
  87.         if(sessionId == null) 
  88.           throw new IOException("Could not find session info"); 
  89.  
  90.         // now, send SETUP commands for each of the tracks 
  91.         int cntOfTracks = tracks.size(); 
  92.         for(int i = 1; i < cntOfTracks; i++) { 
  93.             baseCommand = 
  94.                 getBaseCommand( 
  95.                     "SETUP " + address + "/trackID=" + tracks.elementAt(i)); 
  96.             baseCommand += CRLF + "Session: " + sessionId + CRLF + TRANSPORT_DATA; 
  97.             doCommand(baseCommand); 
  98.         } 
  99.  
  100.         // this is now setup 
  101.         setup = true
  102.     } 
  103.  
  104.     // issues a PLAY command 
  105.     public void doPlay() throws IOException { 
  106.  
  107.         // must be first setup 
  108.         if(!setup) throw new IOException("Not Setup!"); 
  109.  
  110.         // create base command 
  111.         String baseCommand = getBaseCommand("PLAY " + address); 
  112.  
  113.         // add session information 
  114.         baseCommand += CRLF + "Session: " + sessionId; 
  115.  
  116.         // execute it 
  117.         doCommand(baseCommand); 
  118.  
  119.         // set flags 
  120.         playing = true
  121.         stopped = false
  122.     } 
  123.  
  124.     // issues a PAUSE command 
  125.     public void doPause() throws IOException { 
  126.  
  127.         // if it is not playing, do nothing 
  128.         if(!playing) return
  129.  
  130.         // create base command 
  131.         String baseCommand = getBaseCommand("PAUSE " + address); 
  132.  
  133.         // add session information 
  134.         baseCommand += CRLF + "Session: " + sessionId; 
  135.  
  136.         // execute it 
  137.         doCommand(baseCommand); 
  138.  
  139.         // set flags 
  140.         stopped = true
  141.         playing = false
  142.     } 
  143.  
  144.     // issues a TEARDOWN command 
  145.     public void doTeardown() throws IOException { 
  146.  
  147.         // if not setup, nothing to teardown 
  148.         if(!setup) return
  149.  
  150.         // create base command 
  151.         String baseCommand = getBaseCommand("TEARDOWN " + address); 
  152.  
  153.         // add session information 
  154.         baseCommand += CRLF + "Session: " + sessionId; 
  155.  
  156.         // execute it 
  157.         doCommand(baseCommand); 
  158.  
  159.         // set flags 
  160.         described = setup = playing = false
  161.         stopped = true
  162.     } 
  163.  
  164.     // this method is a convenience method to put a RTSP command together 
  165.     private String getBaseCommand(String command) { 
  166.  
  167.         return
  168.             command + 
  169.             " " + 
  170.             VERSION + // version 
  171.             CRLF + 
  172.             "CSeq: " + (CSeq++) // incrementing sequence 
  173.         ); 
  174.     } 
  175.  
  176.     // executes a command and receives response from server 
  177.     private String doCommand(String fullCommand) throws IOException { 
  178.  
  179.         // to read the response from the server 
  180.         byte[] buffer = new byte[2048]; 
  181.  
  182.         // debug 
  183.         System.err.println(" ====== CLIENT REQUEST ====== "); 
  184.         System.err.println(fullCommand + CRLF + CRLF); 
  185.         System.err.println(" ============================ "); 
  186.  
  187.         // send a command 
  188.         os.write((fullCommand + CRLF + CRLF).getBytes()); 
  189.  
  190.         // read response 
  191.         int length = is.read(buffer); 
  192.  
  193.         String response = new String(buffer, 0, length); 
  194.  
  195.         // empty the buffer 
  196.         buffer = null; 
  197.  
  198.         // if the response doesn't start with an all clear 
  199.         if(!response.startsWith(RTSP_OK)) 
  200.           throw new IOException("Server returned invalid code: " + response); 
  201.  
  202.         // debug 
  203.         System.err.println(" ====== SERVER RESPONSE ====== "); 
  204.         System.err.println(response.trim()); 
  205.         System.err.println(" ============================="); 
  206.  
  207.         return response; 
  208.     } 
  209.  
  210.     // convenience method to parse a server response to DESCRIBE command 
  211.     // for track information 
  212.     private void parseTrackInformation(String response) { 
  213.  
  214.         String localRef = response; 
  215.         String trackId = ""
  216.         int index = localRef.indexOf(TRACK_LINE); 
  217.  
  218.         // iterate through the response to find all instances of the 
  219.         // TRACK_LINE, which indicates all the tracks. Add all the 
  220.         // track id's to the tracks vector 
  221.         while(index != -1) { 
  222.             int baseIdx = index + TRACK_LINE.length(); 
  223.             trackId = localRef.substring(baseIdx, baseIdx + 1); 
  224.             localRef = localRef.substring(baseIdx + 1, localRef.length()); 
  225.             index = localRef.indexOf(TRACK_LINE); 
  226.             tracks.addElement(trackId); 
  227.         } 
  228.  
  229.     } 
  230.  
  231.     // find out the session information from the first SETUP command 
  232.     private void parseSessionInfo(String response) { 
  233.  
  234.         sessionId = 
  235.           response.substring( 
  236.                 response.indexOf("Session: ") + "Session: ".length(), 
  237.                 response.indexOf("Date:")).trim(); 
  238.     } 
(feidragon319)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201110/15182.html]
本文出处:blog.csdn.net/feidragon319 作者:feidragon319
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容