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

Experiments in Streaming Content in Java ME(3)

    博客分类:
  • J2ME
阅读更多

Back to RTPSourceStream and StreamingDataSource

With the protocol handler in place, let's revisit the RTPSourceStream and StreamingDataSource classes from earlier, where they contained only place-holder methods. The StreamingDataSource is simple to code:

 

import java.io.IOException;
import javax.microedition.media.Control;
import javax.microedition.media.protocol.DataSource;
import javax.microedition.media.protocol.SourceStream;

public class StreamingDataSource extends DataSource {

  // the full URL like locator to the destination
  private String locator;

  // the internal streams that connect to the source
  // in this case, there is only one
  private SourceStream[] streams;

  // is this connected to its source?
  private Boolean connected = false;

  public StreamingDataSource(String locator) {
      super(locator);
      setLocator(locator);
  }

  public void setLocator(String locator) { this.locator = locator; }

  public String getLocator() { return locator; }

  public void connect() throws IOException {

    // if already connected, return
    if (connected) return;

    // if locator is null, then can't actually connect
    if (locator == null)
      throw new IOException("locator is null");

    // now populate the sourcestream array
    streams = new RTPSourceStream[1];

    // with a new RTPSourceStream
    streams[0] = new RTPSourceStream(locator);

    // set flag
    connected = true;

    }

  public void disconnect() {

    // if there are any streams
    if (streams != null) {

      // close the individual stream
        try {
          ((RTPSourceStream)streams[0]).close();
        } catch(IOException ioex) {} // silent
    }

    // and set the flag
    connected = false;
  }

  public void start() throws IOException {

    if(!connected) return;

    // start the underlying stream
    ((RTPSourceStream)streams[0]).start();

  }

  public void stop() throws IOException {

    if(!connected) return;

    // stop the underlying stream
    ((RTPSourceStream)streams[0])Close();

  }

  public String getContentType() {
    // for the purposes of this article, it is only video/mpeg
    return "video/mpeg";
  }

  public Control[] getControls() { return new Control[0]; }

  public Control getControl(String controlType) { return null; }

  public SourceStream[] getStreams() {    return streams; }

}

 

The main work takes place in the connect() method. It creates a new RTPSourceStream with the requested address. Notice that the getContentType() method returns video/mpeg as the default content type, but change it to the supported content type for your system. Of course, this should not be hard-coded; it should be based on the actual support for different media types.

The next listing shows the complete RTPSourceStream class, which, along with RTSPProtocolHandler, does the bulk of work in connecting getting the RTP packets of the server:

 

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.microedition.io.Datagram;
import javax.microedition.io.Connector;
import javax.microedition.media.Control;
import javax.microedition.io.SocketConnection;
import javax.microedition.io.DatagramConnection;
import javax.microedition.media.protocol.SourceStream;
import javax.microedition.media.protocol.ContentDescriptor;

public class RTPSourceStream implements SourceStream {

    private RTSPProtocolHandler handler;

    private InputStream is;
    private OutputStream Os;

    private DatagramConnection socket;

    public RTPSourceStream(String address) throws IOException {

        // create the protocol handler and set it up so that the
        // application is ready to read data

        // create a socketconnection to the remote host
        // (in this case I have set it up so that its localhost, you can
        // change it to wherever your server resides)
        SocketConnection sc =
          (SocketConnection)Connector.open("socket://localhost:554");

        // open the input and output streams
        is = sc.openInputStream();
        Os = sc.openOutputStream();

        // and initialize the handler
        handler = new RTSPProtocolHandler(address, is, Os);

        // send the basic signals to get it ready
        handler.doDescribe();
        handler.doSetup();
    }

    public void start() throws IOException {

      // open a local socket on port 8080 to read data to
      socket = (DatagramConnection)Connector.open("datagram://:8080");

      // and send the PLAY command
      handler.doPlay();
    }

    public void close() throws IOException {

        if(handler != null) handler.doTeardown();

        is.close();
        os.close();
    }

    public int read(byte[] buffer, int offset, int length)
      throws IOException {

      // create a byte array which will be used to read the datagram
      byte[] fullPkt = new byte[length];

      // the new Datagram
      Datagram packet = socket.newDatagram(fullPkt, length);

      // receive it
      socket.receive(packet);

      // extract the actual RTP Packet's media data in the requested buffer
      RTPPacket rtpPacket = getRTPPacket(packet, packet.getData());
      buffer = rtpPacket.getData();

      // debug
      System.err.println(rtpPacket + " with media length: " + buffer.length);

      // and return its length
      return buffer.length;
    }

    // extracts the RTP packet from each datagram packet received
    private RTPPacket getRTPPacket(Datagram packet, byte[] buf) {

      // SSRC
      long SSRC = 0;

        // the payload type
        byte PT = 0;

      // the time stamp
        int timeStamp = 0;

        // the sequence number of this packet
        short seqNo = 0;


        // see http://www.networksorcery.com/enp/protocol/rtp.htm
        // for detailed description of the packet and its data
        PT =
          (byte)((buf[1] & 0xff) & 0x7f);

        seqNo =
          (short)((buf[2] << 8) | ( buf[3] & 0xff));

        timeStamp =
          (((buf[4] & 0xff) << 24) | ((buf[5] & 0xff) << 16) |
            ((buf[6] & 0xff) << 8) | (buf[7] & 0xff)) ;

        SSRC =
          (((buf[8] & 0xff) << 24) | ((buf[9] & 0xff) << 16) |
            ((buf[10] & 0xff) << 8) | (buf[11] & 0xff));


        // create an RTPPacket based on these values
        RTPPacket rtpPkt = new RTPPacket();

        // the sequence number
        rtpPkt.setSequenceNumber(seqNo);

        // the timestamp
        rtpPkt.setTimeStamp(timeStamp);

        // the SSRC
        rtpPkt.setSSRC(SSRC);

        // the payload type
        rtpPkt.setPayloadType(PT);

        // the actual payload (the media data) is after the 12 byte header
        // which is constant
        byte payload[] = new byte [packet.getLength() - 12];

        for(int i=0; i < payload.length; i++) payload [i] = buf[i+12];

        // set the payload on the RTP Packet
        rtpPkt.setData(payload);

        // and return the payload
        return rtpPkt;

    }

    public long seek(long where) throws IOException {
     throw new IOException("cannot seek");
    }

    public long tell() { return -1; }

    public int getSeekType() { return NOT_SEEKABLE;    }

    public Control[] getControls() { return null; }

    public Control getControl(String controlType) { return null; }

    public long getContentLength() { return -1;    }

    public int getTransferSize() { return -1;    }

    public ContentDescriptor getContentDescriptor() {
        return new ContentDescriptor("audio/rtp");
    }
}

 

The constructor for the RTPSourceStream creates a SocketConnection to the remote server (hard-coded to the local server and port here, but you can change this to accept any server or port). It then opens the input and output streams, which it uses to create the RTSPProtocolHandler. Finally, using this handler, it sends the DESCRIBE and SETUP commands to the remote server to get the server ready to send the packets. The actual delivery doesn't start until the start() method is called by the StreamingDataSource, which opens up a local port (hard-coded to 8081 in this case) for receiving the packets and sends the PLAY command to start receiving these packets. The actual reading of the packets is done in the read() method, which receives the individual packets, strips them to create the RTPPacket instances (with the getRTPPacket() method), and returns the media data in the buffer supplied while calling the read() method.

A MIDlet to see if it works

With all the classes in place, let's write a simple MIDlet to first create a Player instance that will use the StreamingDataSource to connect to the server and then get media packets from it. The Player interface is defined by the MMAPI and allows you to control the playback (or recording) of media. Instances of this interface are created by using the Manager class from the MMAPI javax.microedition.media package (see the MMAPI tutorial). The following shows this rudimentary MIDlet:

 

import javax.microedition.media.Player;
import javax.microedition.midlet.MIDlet;
import javax.microedition.media.Manager;

public class StreamingMIDlet extends MIDlet {

  public void startApp() {

    try {

      // create Player instance, realize it and then try to start it
      Player player =
        Manager.createPlayer(
          new StreamingDataSource(
            "rtsp://localhost:554/sample_100kbit.mp4"));

      player.realize();

      player.start();

    } catch(Exception e) {
            e.printStackTrace();
    }
  }

  public void pauseApp() {}

  public void destroyApp(boolean unconditional) {}
}

 

So what should happen when you run this MIDlet in the Wireless toolkit? I have on purpose left out any code to display the resulting video on screen. When I run it in the toolkit, I know that I am receiving the packets because I see the debug statements as shown in Figure 2.

 

 

Figure 2. Running StreamingMIDlet output

The RTP packets as sent by the server are being received. The StreamingDataSource along with the RTSPProtocolHandler and RTPSourceStream are doing their job of making the streaming server send these packets. This is confirmed by looking at the streaming server's admin console as shown in Figure 3.

 

 

Figure 3. Darwin's admin console shows that the file is being streamed (click for full-size image).

Unfortunately, the player constructed by the Wireless toolkit is trying to read the entire content at one go. Even if I were to make a StreamingVideoControl, it will not display the video until it has read the whole file, therefore defeating the purpose of the streaming aspect of this whole experiment. So what needs to be done to achieve the full streaming experience?

Ideally, MMAPI should provide the means for developers to register the choice of Player for the playback of certain media. This is easily achieved by providing a new method in the Manager class for registering (or overriding) MIME types or protocols with developer-made Player instances. For example, let's say I create a Player instance that reads streaming data called StreamingMPEGPlayer. With the Manager class, I should be able to say Manager.registerPlayer("video/mpeg", StreamingMPEGPlayer.class) or Manager.registerPlayer("rtsp", StreamingMPEGPlayer.class). MMAPI should then simply load this developer-made Player instance and use this as the means to read data from the developer-made datasource.

In a nutshell, you need to be able to create an independent media player and register it as the choice of instance for playing the desired content. Unfortunately, this is not possible with the current MMAPI implementation, and this is the data consumption conundrum that I had talked about earlier.

Of course, if you can test this code in a toolkit that does not need to read the complete data before displaying it (or for audio files, playing them), then you have achieved the aim of streaming data using the existing MMAPI implementation.

This experiment should prove that you can stream data with the current MMAPI implementation, but you may not be able to manipulate it in a useful manner until you have better control over the Manager and Player instances. I look forward to your comments and experiments using this code.

 

 

分享到:
评论

相关推荐

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

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

    【数据分析与科学计算】Anaconda安装与环境管理教程:Python数据科学平台快速入门指南

    内容概要:本文档详细介绍了Anaconda的安装、环境管理和包管理的方法。Anaconda是一个强大的Python数据科学平台,提供了包管理器和环境管理器。安装部分包括了从官网或国内镜像源下载并安装Anaconda,安装时建议修改安装路径并勾选添加环境变量。环境管理方面,涵盖了创建、激活、退出、查看和删除虚拟环境的具体命令。包管理则讲解了在虚拟环境中安装、卸载以及查看已安装包的操作。此外,还提供了配置国内镜像源以提高下载速度的方法,以及一些常用命令与技巧,如更新所有包、导出环境和从配置文件创建环境等。; 适合人群:对Python数据科学感兴趣的初学者,以及需要使用Anaconda进行环境和包管理的开发者。; 使用场景及目标:①帮助用户快速完成Anaconda的安装;②让用户掌握虚拟环境的创建与管理,确保不同项目之间的依赖隔离;③使用户能够熟练地进行包的安装、卸载和更新操作;④提高用户在国内网络环境下获取资源的速度。; 阅读建议:阅读时可结合自身需求重点学习环境管理和包管理的相关命令,对于配置镜像源的内容,可根据自己的网络情况选择是否配置。

    计算机图形学实验一(基本图形生成(一))

    基于visualstudio2010,包括所有源代码,可以运行, 编程实现直线的 DDA 算法及 Bresenham 算法绘制任意斜率的直线。 设计一个图形并调用 1 中的 Bresenham 算法程序绘制。

    3D空间避障与路径规划:RRT与RRT算法的MATLAB实现及应用 3D建模 基于RRT和RRT算法的3D场景路径规划与障碍物距离分析

    内容概要:本文详细介绍了RRT(快速随机树)和RRT*算法在3D场景下的应用,重点在于如何绕过两个圆柱障碍物到达目标点。文中通过MATLAB代码实现了路径规划的具体步骤,包括初始化参数、随机采样、寻找最近节点、扩展树结构、判断是否绕过障碍物以及输出路径图和路径点与障碍物最小距离变化图。此外,还对算法进行了简要介绍,指出了其优点和局限性。 适合人群:从事机器人技术、自动化控制、机械臂路径规划的研究人员和技术人员,尤其是对3D空间避障与路径规划感兴趣的开发者。 使用场景及目标:①帮助研究人员理解和实现RRT和RRT*算法在3D环境中的具体应用;②为移动机器人和机械臂的路径规划提供理论支持和实践指导;③通过图示和代码示例,使读者能够更好地掌握算法的实现细节。 其他说明:虽然RRT和RRT*算法在处理复杂环境下的路径规划问题时表现出色,但也存在一些局限性,如可能陷入局部最优解等问题。未来可以通过改进算法来提升其性能和适用性。

    科普内容创作者科普文章AI写作提示词科普论文写作提示词(AI提示词Prompt)

    科普内容创作者科普文章AI写作提示词科普论文写作提示词(AI提示词Prompt)

    一种新型具有多陷波特性的超宽带天线.zip

    一种新型具有多陷波特性的超宽带天线.zip

    cmd-bat-批处理-脚本-vcvars32.zip

    cmd-bat-批处理-脚本-vcvars32.zip

    模块化SOC主动均衡模型:六节电池串联系统的充放电均衡解决方案 电池管理系统

    内容概要:本文介绍了一种专为六节电池串联设计的模块化SOC主动均衡模型。该模型采用底层双向反激变换器和顶层buck-boost均衡的双重策略,旨在解决电池组中各节电池SOC不一致的问题。通过模块化设计,模型实现了灵活性和扩展性,适用于不同类型的电池组。文章详细介绍了模型的工作原理、设计思路以及仿真实验结果,验证了模型的有效性。 适合人群:从事电池管理系统的研发人员、电力电子工程师、科研工作者。 使用场景及目标:①研究电池组充放电均衡技术;②优化电池管理系统的设计;③作为论文创新和仿真实验的基础。 阅读建议:重点理解双向反激变换器和buck-boost均衡的具体实现方法及其协同工作的机制,结合仿真实验数据进一步验证模型效果。

    cmd-bat-批处理-脚本-LaunchDevCmd.zip

    cmd-bat-批处理-脚本-LaunchDevCmd.zip

    digital-clock-top24.bit

    数字钟实验24小时制bit文件

    2025年系统集成项目管理工程师考试大纲及培训指南2.doc

    2025年系统集成项目管理工程师考试大纲及培训指南2.doc

    校园网络课程设计项目全面规划

    校园网络课程设计

    实证数据-1995-2022年网络媒体关注度、报刊媒体关注度与媒体监督相关数据-社科经管.rar

    该数据集收录了1995-2022年期间中国网络媒体与报刊媒体关注度及媒体监督相关的实证研究数据,适用于社会科学与经济管理领域研究。数据内容包含以下三方面指标: 媒体关注度指标:包括标题及内容中出现特定主体的新闻总数,按正、中、负面情感分类统计,并区分原创与非原创内容。例如,标题提及主体的新闻总量、内容提及主体的新闻总量,以及正面/中性/负面新闻的原创数量。 媒体监督指标:采用Janis-Fadner系数(J-F系数)量化媒体监督力度,基于媒体报道的正面与负面内容比例计算,反映媒体对企业、事件或话题的舆论监督强度。 数据类型与结构:提供原始数据、参考文献、统计代码(Do文件)及处理后的结构化数据,覆盖时间跨度达28年。数据来源于公开新闻报道及第三方平台监测,涵盖网络媒体(如门户网站、社交媒体)与报刊媒体的多维信息。 该数据集可用于分析媒体舆论对经济主体行为的影响、风险抑制机制研究(如企业违规行为与媒体曝光的关联性)、舆情传播特征与社会治理等领域,为实证研究提供基础数据支持。数据格式兼容主流统计软件,便于直接应用于计量模型分析。

    cmd-bat-批处理-脚本-showWLAN.zip

    cmd-bat-批处理-脚本-showWLAN.zip

    F0C-stm32f103c8t6-DRV8313-AS5600-2804无刷电机.rar

    @吾乃周树人中人 【自制】STM32无刷电机FOC驱动一体板---无刷电机能放歌?【FOC三环控制】 (https://www.bilibili.com/video/BV17FUeYQEXa/?spm_id_from=333.1387.favlist.content.click&vd_source=b344881caf56010b57ef7c87acf3ec92) 这是一块集成驱动、控制、CAN通信、磁编码器、还有电流检测的无刷电机驱动板 功能1:低速高扭矩模式 功能2:双电机同步模式 功能3:力反馈模式 功能4:速度环+位置环模式 功能5:速度环+电流环模式 功能6:指尖陀螺模式 功能7:指尖陀螺plus模式 功能8:阻尼旋钮模式 功能9:多档开关模式 功能10:播放音乐 主控:STM32F103C8T6 使用Hal库 磁编码芯片:AS5600 IIC通信 电流检测芯片:两路 INA240 模拟量输入 can芯片:TJA1050 有感电机:需要在电机轴上固定径向磁铁 供电电压:12V

    NVIDIA DLI 深度学习基础课程证书领取答案

    NVIDIA深度学习学院(DLI)是一个专注于深度学习、加速计算和人工智能领域的培训与认证平台。通过其丰富的学习资源,学员可以掌握构建、训练和部署神经网络等核心技能。本文将重点介绍NVIDIA DLI深度学习基础课程中的关键知识点,包括深度学习模型的构建、数据预处理等。 模型构建 在代码示例中,使用TensorFlow的Keras模块导入了预训练的VGG16模型作为基础架构。 关键点包括: 使用keras.applications.VGG16加载VGG16模型,其中weights="imagenet"表示使用基于ImageNet数据集的预训练权重;input_shape=(224,224,3)指定了输入图像的尺寸为224×224像素,且为三通道(RGB);include_top=False意味着不包含原始模型的顶层全连接层。 通过base_model.trainable = False冻结基础模型的所有层,确保在后续训练中这些层的权重不会更新。 构建新模型时,创建输入层inputs = keras.Input(shape=(224,224,3)),利用基础模型提取特征x = base_model(inputs, training=False),随后添加全局平均池化层x = keras.layers.GlobalAveragePooling2D()(x),并在最后添加一个具有6个节点的分类层outputs = keras.layers.Dense(6, activation="softmax")(x),适用于多分类任务。 使用model.summary()查看模型的结构细节。 编译模型时,选择交叉熵损失函数loss="categorical_crossentropy"、Adam优化器optimizer="adam"以及准确率metrics=["accuracy"]作为评估指标

    基于变值测量模型的心电信号可视化研究.zip

    基于变值测量模型的心电信号可视化研究.zip

    基于Java与SQL Server的学生成绩管理应用开发

    《Java+SQL Server学生成绩管理系统》是一款融合了Java编程语言与SQL Server数据库技术的软件,专为高校或教育机构设计,用于高效管理学生的考试成绩。它具备数据录入、查询、统计分析等功能,旨在提升教学管理效率。 该系统的核心技术包括:一是Java后端开发。Java承担后端任务,处理HTTP请求、实现业务逻辑以及与数据库交互。项目可能借助Spring框架,利用其依赖注入、AOP等特性,简化开发流程。Spring MVC则助力实现MVC模式,处理Web请求。二是JSP技术。JSP是一种动态网页技术,页面融合HTML、CSS、JavaScript和Java代码,用于呈现用户界面及处理前端简单逻辑。三是SQL Server数据库。作为数据存储后端,SQL Server支持通过SQL语句完成表的创建、数据的增删改查等操作,还可借助存储过程和触发器提升功能与安全性。四是数据库设计。系统数据库设计关键,包含“学生”“课程”“成绩”等表,通过主外键关联数据,如“学生”表与“成绩”表通过学生ID关联,“课程”表与“成绩”表通过课程ID关联。五是B/S架构。采用浏览器/服务器架构,用户经Web浏览器访问系统,计算与数据处理在服务器端完成,降低客户端硬件要求。六是安全性。系统设置身份验证与授权机制,如登录验证,确保信息访问安全。同时,为防范SQL注入等威胁,采用预编译语句或ORM框架处理SQL命令。七是异常处理。Java的异常处理机制可捕获运行时问题,保障系统稳定性与用户体验。八是报表统计功能。系统具备成绩统计功能,如计算平均分、排名、及格率等,常使用聚合函数(SUM、AVG、COUNT等)和GROUP BY语句。九是设计模式。开发中可能运用单例模式、工厂模式等,提升代码可维护性和复用性。十是版本控制。项目可能使用Git等版本控制系统,便于团队协作与代码管理。 该学生成绩管理

    cmd-bat-批处理-脚本-semver.zip

    cmd-bat-批处理-脚本-semver.zip

    cmd-bat-批处理-脚本-rems.zip

    cmd-bat-批处理-脚本-rems.zip

Global site tag (gtag.js) - Google Analytics