六狼论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博账号登陆

只需一步,快速开始

搜索
查看: 30|回复: 0

Experiments in Streaming Content in Java ME(2)

[复制链接]

升级  93.33%

52

主题

52

主题

52

主题

秀才

Rank: 2

积分
190
 楼主| 发表于 2013-1-28 19:26:04 | 显示全部楼层 |阅读模式
Creating an RTSP Protocol Handler

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 (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.0CSeq: 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.0CSeq: 2TRANSPORT: 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.0CSeq: 3Session: 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:
 
import java.util.Vector;import java.io.InputStream;import java.io.IOException;import java.io.OutputStream;public class RTSPProtocolHandler {    // the address of the media file as an rtsp://... String    private String address;    // the inputstream to receive response from the server    private InputStream is;    // the outputstream to write to the server    private OutputStream os;    // the incrementing sequence number for each request    // sent by the client    private static int CSeq = 1;    // the session id sent by the server after an initial setup    private String sessionId;    // the number of tracks in a media file    private Vector tracks = new Vector(2);    // flags to indicate the status of a session    private boolean described, setup, playing;    private Boolean stopped = true;    // constants    private static final String CRLF = "\r\n";    private static final String VERSION = "rtsp/1.0";    private static final String TRACK_LINE = "a=control:trackID=";    private static final String TRANSPORT_DATA =      "TRANSPORT: UDP;unicast;client_port=8080-8081";    private static final String RTSP_OK = "RTSP/1.0 200 OK";    // base constructor, takes the media address, input and output streams    public RTSPProtocolHandler(        String address, InputStream is, OutputStream Os) {        this.address = address;        this.is = is;        this.os = Os;    }    // creates, sends and parses a DESCRIBE client request    public void doDescribe() throws IOException {        // if already described, return        if(described) return;        // create the base command        String baseCommand = getBaseCommand("DESCRIBE " + address);        // execute it and read the response        String response = doCommand(baseCommand);        // the response will contain track information, amongst other things        parseTrackInformation(response);        // set flag        described = true;    }    // creates, sends and parses a SETUP client request    public void doSetup() throws IOException {        // if not described        if(!described) throw new IOException("Not Described!");        // create the base command for the first SETUP track        String baseCommand =          getBaseCommand(                "SETUP " + address + "/trackID=" + tracks.elementAt(0));        // add the static transport data        baseCommand += CRLF + TRANSPORT_DATA;        // read response        String response = doCommand(baseCommand);        // parse it for session information        parseSessionInfo(response);        // if session information cannot be parsed, it is an error        if(sessionId == null)          throw new IOException("Could not find session info");        // now, send SETUP commands for each of the tracks        int cntOfTracks = tracks.size();        for(int i = 1; i < cntOfTracks; i++) {            baseCommand =                getBaseCommand(                    "SETUP " + address + "/trackID=" + tracks.elementAt(i));            baseCommand += CRLF + "Session: " + sessionId + CRLF + TRANSPORT_DATA;            doCommand(baseCommand);        }        // this is now setup        setup = true;    }    // issues a PLAY command    public void doPlay() throws IOException {        // must be first setup        if(!setup) throw new IOException("Not Setup!");        // create base command        String baseCommand = getBaseCommand("PLAY " + address);        // add session information        baseCommand += CRLF + "Session: " + sessionId;        // execute it        doCommand(baseCommand);        // set flags        playing = true;        stopped = false;    }    // issues a PAUSE command    public void doPause() throws IOException {        // if it is not playing, do nothing        if(!playing) return;        // create base command        String baseCommand = getBaseCommand("PAUSE " + address);        // add session information        baseCommand += CRLF + "Session: " + sessionId;        // execute it        doCommand(baseCommand);        // set flags        stopped = true;        playing = false;    }    // issues a TEARDOWN command    public void doTeardown() throws IOException {        // if not setup, nothing to teardown        if(!setup) return;        // create base command        String baseCommand = getBaseCommand("TEARDOWN " + address);        // add session information        baseCommand += CRLF + "Session: " + sessionId;        // execute it        doCommand(baseCommand);        // set flags        described = setup = playing = false;        stopped = true;    }    // this method is a convenience method to put a RTSP command together    private String getBaseCommand(String command) {        return(            command +            " " +            VERSION + // version            CRLF +            "CSeq: " + (CSeq++) // incrementing sequence        );    }    // executes a command and receives response from server    private String doCommand(String fullCommand) throws IOException {        // to read the response from the server        byte[] buffer = new byte[2048];        // debug        System.err.println(" ====== CLIENT REQUEST ====== ");        System.err.println(fullCommand + CRLF + CRLF);        System.err.println(" ============================ ");        // send a command        os.write((fullCommand + CRLF + CRLF).getBytes());        // read response        int length = is.read(buffer);        String response = new String(buffer, 0, length);        // empty the buffer        buffer = null;        // if the response doesn't start with an all clear        if(!response.startsWith(RTSP_OK))          throw new IOException("Server returned invalid code: " + response);        // debug        System.err.println(" ====== SERVER RESPONSE ====== ");        System.err.println(response.trim());        System.err.println(" =============================");        return response;    }    // convenience method to parse a server response to DESCRIBE command    // for track information    private void parseTrackInformation(String response) {        String localRef = response;        String trackId = "";        int index = localRef.indexOf(TRACK_LINE);        // iterate through the response to find all instances of the        // TRACK_LINE, which indicates all the tracks. Add all the        // track id's to the tracks vector        while(index != -1) {            int baseIdx = index + TRACK_LINE.length();            trackId = localRef.substring(baseIdx, baseIdx + 1);            localRef = localRef.substring(baseIdx + 1, localRef.length());            index = localRef.indexOf(TRACK_LINE);            tracks.addElement(trackId);        }    }    // find out the session information from the first SETUP command    private void parseSessionInfo(String response) {        sessionId =          response.substring(                response.indexOf("Session: ") + "Session: ".length(),                response.indexOf("Date:")).trim();    }} 
您需要登录后才可以回帖 登录 | 立即注册 新浪微博账号登陆

本版积分规则

快速回复 返回顶部 返回列表