`
fonter
  • 浏览: 869205 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Experiments in Streaming Content in Java ME(2)

    博客分类:
  • J2ME
阅读更多

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.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:

 

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();

    }

}

 

分享到:
评论

相关推荐

    Experiments in Streaming Content in Java ME(源码下载)

    标题“Experiments in Streaming Content in Java ME(源码下载)”涉及的是在Java ME环境中实现流媒体内容的实验。Java ME(Micro Edition)是Java平台的一个版本,主要用于移动设备和嵌入式系统。这个项目可能专注于...

    Vehicles: Experiments in Synthetic Psychology

    这本书对反应式机器人和行为式机器人有各种介绍,对智能机器人的爱好者是一个很不错的启发学习。

    [Valentino_Braitenberg]_Vehicles_Experiments_in_Synthetic Psychology

    《车辆:合成心理学的实验》是由Valentino Braitenberg所著的一本经典著作,它深入探讨了人工生命和智能机器人的概念,特别是在心理学视角下的机器行为。这本书的核心是Braitenberg小车,这是一种简单的机器人设计,...

    java8stream源码-java-flow-experiments:在Java8和Java9中试验ReactiveStreams。这个存储

    java8流源码java-flow-experiments 在这个存储库中,我正在试验 ,它将成为 Java 8 应用程序java.util.concurrent包中的 Java 9 的一部分。 这个存储库是为我的主题为 java.util.concurrent.Flow 做好准备的演讲准备...

    Mastering Java Machine Learning

    ideally, intermediate-level data analysts and data scientists with experience in Java. Preferably, you will have experience with the fundamentals of machine learning and now have a desire to explore ...

    The Puzzle of Thought Experiments in Conceptual

    2. **棱镜式再评估**:通过不同的视角重新审视原始假设和结果,可以帮助识别潜在的偏差或盲点。这种方法类似于将光分解成不同颜色的棱镜,从而揭示出更多的细节。 3. **回顾式再评估**:随着时间的推移,新的经验和...

    Design and Analysis of Experiments vol.2.pdf

    【标题】《Design and Analysis of Experiments vol.2.pdf》 知识点: 1. 文件标题暗示这是一本关于实验设计和分析的书籍,具体是其第二卷。 2. “Advanced Experimental Design”表明这本书聚焦于实验设计的高级...

    历年考研英语全国统考习题及答案

    1 the fruit-fly experiments described in Carl Zimmer’s piece in the Science Times on Tuesday. Fruit flies who were taught to be smarter than the average fruit fly 2 to live shorter lives. This ...

    实验设计与分析第一门课程A First Course in Design and Analysis of Experiments

    - 《实验设计:理论与应用》(Design and Analysis of Experiments)由Douglas C. Montgomery著。 - 《统计学原理》(Principles of Statistics)等相关教材。 通过以上概述,我们可以看到《实验设计与分析第一门...

    Java 9 with JShell

    The release of Java 9 has brought many subtle and not-so-subtle changes to the way in which Java programmers approach their code. The most important ones are definitely the availability of a REPL, ...

    Design of Experiments for Engineers and Scientists-Elsevier (2014)

    Design of Experiments for Engineers and Scientists Second Edition Design of Experiments (DOE) is a powerful technique used for both exploring new processes and gaining increased knowledge of existing ...

    Experiments in segmentation using a facet model region grower

    2. **构建结构**: - **区域属性列表**:记录每个分割出的子区域的属性信息,如面积、形状、灰度均值等。 - **区域邻接图**:表示各个子区域之间的空间关系,便于后续处理时考虑邻近性因素。 3. **全局信息利用**...

    java第二次试验

    In conclusion, this Java laboratory report covers fundamental programming structures in Java, including data types, variables, assignments, and operators. The three experiments demonstrate the ...

    gradle_java_experiments:各种gradle java实验的容器项目

    "gradle_java_experiments"项目是一个专注于探索Gradle与Java结合使用的实验性项目,旨在帮助开发者深入理解这两个工具的协同工作方式。 **Gradle特性与优势** 1. **灵活的构建模型**:Gradle基于Groovy或Kotlin ...

    ShearLab3D_Experiments_2D.zip

    2. **图像压缩**:Shearlet变换后的系数分布具有稀疏性,这意味着大部分信息可以用少量的系数表示,这为高效的图像压缩提供了可能。相比于传统的JPEG或JPEG2000,Shearlet压缩可以实现更高的压缩比且保持较好的图像...

    Randomized Experiments from Non-random Selection in U.S.,.pdf

    David S. Lee:Randomized Experiments from Non-random Selection in U.S. House Elections._ Journal of Econometrics,

    离散数学,逻辑和可计算性的Prolog实验Prolog Experiments in Discrete Math, Logic, and Computability

    在IT领域,Prolog(Programming in Logic)是一种专门用于逻辑编程的高级编程语言,常用于人工智能领域的研究与开发。 在本书的介绍部分,作者首先带领读者入门Prolog语言,随后以一系列实验加深对离散数学、逻辑...

    Java8Experiments:酿什么呢? Java8是!

    Java8实验 酿什么呢? Java8是! 这个实验项目深入到Java8中,并探讨了其中引入的一些非常酷的功能。 该代码包含以下Java8功能: 内置功能接口 谓词 职能 溪流 map() reduce filter collect 自定义功能界面 ...

    Design And Analysis Of Experiments

    Design And Analysis Of Experiments, 5Th Edition (Douglas C Montgomery)-Keep 试验设计与分析 5ed

Global site tag (gtag.js) - Google Analytics