`
fonter
  • 浏览: 870721 次
  • 性别: 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平台的一个版本,主要用于移动设备和嵌入式系统。这个项目可能专注于...

    无需编写任何代码即可创建应用程序:Deepseek-R1 和 RooCode AI 编码代理.pdf

    deepseek最新资讯、配置方法、使用技巧,持续更新中

    Heric拓扑并网离网仿真模型:PR单环控制,SogIPLL锁相环及LCL滤波器共模电流抑制技术解析,基于Heric拓扑的离网并网仿真模型研究与应用分析:PR单环控制与Sogipll锁相环的共模电流抑

    Heric拓扑并网离网仿真模型:PR单环控制,SogIPLL锁相环及LCL滤波器共模电流抑制技术解析,基于Heric拓扑的离网并网仿真模型研究与应用分析:PR单环控制与Sogipll锁相环的共模电流抑制效能,#Heric拓扑并离网仿真模型(plecs) 逆变器拓扑为:heric拓扑。 仿真说明: 1.离网时支持非单位功率因数负载。 2.并网时支持功率因数调节。 3.具有共模电流抑制能力(共模电压稳定在Udc 2)。 此外,采用PR单环控制,具有sogipll锁相环,lcl滤波器。 注:(V0004) Plecs版本4.7.3及以上 ,Heric拓扑; 离网仿真; 并网仿真; 非单位功率因数负载; 功率因数调节; 共模电流抑制; 共模电压稳定; PR单环控制; sogipll锁相环; lcl滤波器; Plecs版本4.7.3及以上,Heric拓扑:离网并网仿真模型,支持非单位功率因数与共模电流抑制

    培训机构客户管理系统 2024免费JAVA微信小程序毕设

    2024免费微信小程序毕业设计成品,包括源码+数据库+往届论文资料,附带启动教程和安装包。 启动教程:https://www.bilibili.com/video/BV1BfB2YYEnS 讲解视频:https://www.bilibili.com/video/BV1BVKMeZEYr 技术栈:Uniapp+Vue.js+SpringBoot+MySQL。 开发工具:Idea+VSCode+微信开发者工具。

    基于SMIC 40nm工艺库的先进芯片技术,SMIC 40nm工艺库技术细节揭秘:引领半导体产业新革命,smic40nm工艺库 ,smic40nm; 工艺库; 芯片制造; 纳米技术,SMIC 40nm

    基于SMIC 40nm工艺库的先进芯片技术,SMIC 40nm工艺库技术细节揭秘:引领半导体产业新革命,smic40nm工艺库 ,smic40nm; 工艺库; 芯片制造; 纳米技术,SMIC 40nm工艺库:领先技术驱动的集成电路设计基础

    2013年上半年软件设计师上午题-真题及答案解析

    2013年上半年软件设计师上午题-真题及答案解析

    淮南市乡镇边界,shp格式

    shp格式,可直接导入arcgis使用

    ROS下的移动机器人路径规划算法:基于强化学习算法DQN、DDPG、SAC及TD3的实践与应用,ROS系统中基于强化学习算法的移动机器人路径规划策略研究:应用DQN、DDPG、SAC及TD3算法,RO

    ROS下的移动机器人路径规划算法:基于强化学习算法DQN、DDPG、SAC及TD3的实践与应用,ROS系统中基于强化学习算法的移动机器人路径规划策略研究:应用DQN、DDPG、SAC及TD3算法,ROS下的移动机器人路径规划算法,使用的是 强化学习算法 DQN DDPG SAC TD3等 ,ROS; 移动机器人; 路径规划算法; DQN; DDPG; SAC; TD3,ROS强化学习移动机器人路径规划算法研究

    粒子群优化算法精准辨识锂电池二阶RC模型参数:高仿真精度下的SOC估计铺垫,粒子群优化算法精准辨识锂电池二阶RC模型参数:仿真验证与SOC估计铺垫,使用粒子群优化算法(PSO)辨识锂电池二阶RC模型参

    粒子群优化算法精准辨识锂电池二阶RC模型参数:高仿真精度下的SOC估计铺垫,粒子群优化算法精准辨识锂电池二阶RC模型参数:仿真验证与SOC估计铺垫,使用粒子群优化算法(PSO)辨识锂电池二阶RC模型参数(附MATLAB代码) 使用粒子群优化算法来辨识锂离子电池二阶RC模型的参数。 将粒子群优化算法寻找到的最优参数代入二阶RC模型进行仿真,经过验证,端电压的估计误差小于0.1%,说明粒子群优化算法辨识得到的参数具有较高的精度,为锂离子电池SOC的估计做铺垫。 ,关键词:粒子群优化算法(PSO); 锂电池二阶RC模型参数辨识; MATLAB代码; 端电压估计误差; 锂离子电池SOC估计。,PSO算法优化锂电池二阶RC模型参数:高精度仿真与MATLAB代码实现

    selenium环境搭建-谷歌浏览器驱动

    selenium环境搭建-谷歌浏览器驱动

    35页-华为智慧社区商业解决方案.pdf

    在当今科技日新月异的时代,智慧社区的概念正悄然改变着我们的生活方式。它不仅仅是一个居住的空间,更是一个集成了先进科技、便捷服务与人文关怀的综合性生态系统。以下是对智慧社区整体解决方案的精炼融合,旨在展现其知识性、趣味性与吸引力。 一、智慧社区的科技魅力 智慧社区以智能化设备为核心,通过综合运用物联网、大数据、云计算等技术,实现了社区管理的智能化与高效化。门禁系统采用面部识别技术,让居民无需手动操作即可轻松进出;停车管理智能化,不仅提高了停车效率,还大大减少了找车位的烦恼。同时,安防报警系统能够实时监测家中安全状况,一旦有异常情况,立即联动物业进行处理。此外,智能家居系统更是将便捷性发挥到了极致,通过手机APP即可远程控制家中的灯光、窗帘、空调等设备,让居民随时随地享受舒适生活。 视频监控与可视对讲系统的结合,不仅提升了社区的安全系数,还让居民能够实时查看家中情况,与访客进行视频通话,大大增强了居住的安心感。而电子巡更、公共广播等系统的运用,则进一步保障了社区的治安稳定与信息传递的及时性。这些智能化设备的集成运用,不仅提高了社区的管理效率,更让居民感受到了科技带来的便捷与舒适。 二、智慧社区的增值服务与人文关怀 智慧社区不仅仅关注科技的运用,更注重为居民提供多元化的增值服务与人文关怀。社区内设有互动LED像素灯、顶层花园控制喷泉等创意设施,不仅美化了社区环境,还增强了居民的归属感与幸福感。同时,社区还提供了智能家居的可选追加项,如空气净化器、远程监控摄像机等,让居民能够根据自己的需求进行个性化选择。 智慧社区还充分利用大数据技术,对居民的行为数据进行收集与分析,为居民提供精准化的营销服务。无论是周边的商业信息推送,还是个性化的生活建议,都能让居民感受到社区的智慧与贴心。此外,社区还注重培养居民的环保意识与节能意识,通过智能照明、智能温控等系统的运用,鼓励居民节约资源、保护环境。 三、智慧社区的未来发展与无限可能 智慧社区的未来发展充满了无限可能。随着技术的不断进步与创新,智慧社区将朝着更加智能化、融合化的方向发展。比如,利用人工智能技术进行社区管理与服务,将能够进一步提升社区的智能化水平;而5G、物联网等新技术的运用,则将让智慧社区的连接更加紧密、服务更加高效。 同时,智慧社区还将更加注重居民的体验与需求,通过不断优化智能化设备的功能与服务,让居民享受到更加便捷、舒适的生活。未来,智慧社区将成为人们追求高品质生活的重要选择之一,它不仅是一个居住的空间,更是一个融合了科技、服务、人文关怀的综合性生态系统,让人们的生活更加美好、更加精彩。 综上所述,智慧社区整体解决方案以其科技魅力、增值服务与人文关怀以及未来发展潜力,正吸引着越来越多的关注与认可。它不仅能够提升社区的管理效率与居民的生活品质,更能够为社区的可持续发展注入新的活力与动力。

    PowerSettingsExplorer.rar

    PowerSettingsExplorer.rar 电脑的电源管理软件,明白的不多说。自己搜索即可知道。

    2025年开源人工智能:关键参与者与预测.pdf

    deepseek最新资讯,配置方法,使用技巧,持续更新中

    DeepSeek 发布 Janus Pro AI 图像生成器 – 开源且免费.pdf

    deepseek最新资讯、配置方法、使用技巧,持续更新中

    消息中间件rabbitmq-server

    RabbitMQ 是一个开源的消息代理(Message Broker),实现了 AMQP(Advanced Message Queuing Protocol) 协议,用于在分布式系统中实现高效、可靠的消息传递。

    西门子S7-1200与汇川PLC新通信选择:Ethernet IP通信的突破与优势,功能安全及精准同步的创新实践 ,西门子S7-1200与汇川PLC通信新选择:Ethernet IP通信方案亮相,替代

    西门子S7-1200与汇川PLC新通信选择:Ethernet IP通信的突破与优势,功能安全及精准同步的创新实践。,西门子S7-1200与汇川PLC通信新选择:Ethernet IP通信方案亮相,替代Modbus TCP实现更高级功能与安全控制。,西门子PLC和汇川PLC新通信选择-西门子S7-1200 1500系列PLC也开始支持Ethernet IP通信了。 这为西门子系列的PLC和包括汇川AM400 600等Codesys系PLC的通信提供了新的解决方案。 当前两者之间的通信大多采用ModBus TCP通信。 Modbus TCP和EtherNet IP的区别主要是应用层不相同,ModbusTCP的应用层采用Modbus协议,而EtherNetIP采用CIP协议,这两种工业以太网的数据链路层采用的是CSMACCD,因此是标准的以太网,另外,这两种工业以太网的网络层和传输层采用TCPIP协议族。 还有一个区别是,Modbus协议中迄今没有协议来完成功能安全、高精度同步和运功控制等,而EtherNet IP有CIPSatety、ClIP Sync和ClPMotion来

    自适应无迹卡尔曼滤波AUKF算法:系统估计效果展示与特性分析(含MATLAB代码与Excel数据),自适应无迹卡尔曼滤波AUKF算法:系统估计效果展示与特性分析(含MATLAB代码与Excel数据)

    自适应无迹卡尔曼滤波AUKF算法:系统估计效果展示与特性分析(含MATLAB代码与Excel数据),自适应无迹卡尔曼滤波AUKF算法:系统估计效果展示与特性分析(含MATLAB代码与Excel数据),自适应无迹卡尔曼滤波AUKF算法 配套文件包含MATLAB代码+excel数据+学习资料 估计效果与系统特性有关,图片展示为一复杂系统估计效果 ,AUKF算法; MATLAB代码; excel数据; 学习资料; 估计效果; 系统特性。,自适应无迹卡尔曼滤波AUKF算法:MATLAB代码与学习资料

    基于MATLAB Simscape的IGBT开关特性模型:揭示开关损耗、米勒平台及瞬态行为的分析工具,IGBT开关特性模型与MATLAB Simscape模拟:深入理解开关行为及损耗数据,IGBT开关

    基于MATLAB Simscape的IGBT开关特性模型:揭示开关损耗、米勒平台及瞬态行为的分析工具,IGBT开关特性模型与MATLAB Simscape模拟:深入理解开关行为及损耗数据,IGBT开关特性模型,MATLAB Simscape模型。 该模型展示了IGBT的详细的开关模型,用于创建开关损耗列表数据。 有助于理解IGBT米勒平台、瞬态开关行为。 也可以用于MOOSFET。 ,IGBT开关模型; MATLAB Simscape; 开关损耗; 米勒平台; 瞬态开关行为; MOOSFET。,MATLAB Simscape中IGBT精细开关模型:揭示米勒平台与瞬态行为

    基于卷积神经网络CNN的多输入单输出数据回归预测-含详细注释与多种评估指标(R2、MAE、MBE),基于卷积神经网络CNN的多输入单输出数据回归预测模型详解-附代码注释、指标评估及Excel数据处

    基于卷积神经网络CNN的多输入单输出数据回归预测——含详细注释与多种评估指标(R2、MAE、MBE),基于卷积神经网络CNN的多输入单输出数据回归预测模型详解——附代码注释、指标评估及Excel数据处理方法,基于卷积神经网络CNN的数据回归预测 多输入单输出预测 代码含详细注释,不负责 数据存入Excel,替方便,指标计算有决定系数R2,平均绝对误差MAE,平均相对误差MBE ,基于卷积神经网络CNN; 数据回归预测; 多输入单输出; 详细注释; Excel存储; 指标计算(R2; MAE; MBE); 不负责。,基于CNN的卷积数据回归预测模型:多输入单输出代码详解与性能评估

    四六级词汇系统 2024免费JAVA微信小程序毕设

    2024免费微信小程序毕业设计成品,包括源码+数据库+往届论文资料,附带启动教程和安装包。 启动教程:https://www.bilibili.com/video/BV1BfB2YYEnS 讲解视频:https://www.bilibili.com/video/BV1BVKMeZEYr 技术栈:Uniapp+Vue.js+SpringBoot+MySQL。 开发工具:Idea+VSCode+微信开发者工具。

Global site tag (gtag.js) - Google Analytics