【问题标题】:RTP on Android MediaPlayerAndroid MediaPlayer 上的 RTP
【发布时间】:2011-07-30 21:10:13
【问题描述】:

我在 Android MediaPlayer 上使用 VLC 作为 rtsp 实现了 RTSP 使用此代码的服务器:

# vlc -vvv /home/marco/Videos/pippo.mp4 --sout 
#rtp{dst=192.168.100.246,port=6024-6025,sdp=rtsp://192.168.100.243:8080/test.sdp}

在 Android 项目上:


Uri videoUri = Uri.parse("rtsp://192.168.100.242:8080/test.sdp"); 
videoView.setVideoURI(videoUri); 
videoView.start(); 

这很好,但如果我还想播放实时流 RTP,所以我 将 sdp 文件复制到 sdcard (/mnt/sdcard/test.sdp) 并设置 vlc:

# vlc -vvv /home/marco/Videos/pippo.mp4 --sout 
#rtp{dst=192.168.100.249,port=6024-6025} 

我尝试播放流RTP设置sdp文件的路径 本地:


Uri videoUri = Uri.parse("/mnt/sdcard/test.sdp");
videoView.setVideoURI(videoUri); 
videoView.start(); 

但我得到一个错误:


D/MediaPlayer( 9616): Couldn't open file on client side, trying server side 
W/MediaPlayer( 9616): info/warning (1, 26) 
I/MediaPlayer( 9616): Info (1,26) 
E/PlayerDriver(   76): Command PLAYER_INIT completed with an error or info PVMFFailure 
E/MediaPlayer( 9616): error (1, -1)
E/MediaPlayer( 9616): Error (1,-1) 
D/VideoView( 9616): Error: 1,-1 

有谁知道问题出在哪里?我错了或者不可能 在 MediaPlayer 上播放 RTP? 干杯 乔治

【问题讨论】:

    标签: android media-player rtp


    【解决方案1】:

    我有一个部分解决方案。

    我目前正在从事一个 Ra&D 项目,该项目涉及从服务器到 Android 客户端的 RTP 媒体流。

    通过这项工作,我为我自己的名为 smpte2022lib 的库做出了贡献,您可以在这里找到: http://sourceforge.net/projects/smpte-2022lib/.

    借助此类库(Java 实现是目前最好的),您可能能够解析来自专业流媒体设备、VLC RTP 会话的 RTP 多播流......

    我已经使用来自使用 SMPTE-2022 2D-FEC 捕获的专业 RTP 流的流或使用 VLC 生成的简单流成功地测试了它。

    很遗憾,我不能在此处放置 code-sn-p,因为使用它的项目实际上受版权保护,但我确保您可以通过 RtpPacket 构造函数帮助解析 UDP 流来简单地使用它。

    如果数据包是有效的 RTP 数据包(字节),它们将被解码。

    此时,我将对 RtpPacket 的构造函数的调用包装到一个线程,该线程实际上将解码的有效负载存储为媒体文件。然后我会以这个文件为参数调用VideoView。

    交叉手指;-)

    亲切的问候,

    大卫·费舍尔

    【讨论】:

      【解决方案2】:

      可能在 android 中使用(不是 mediaPlayer,而是其他更底层的东西),但是当媒体生态系统的其他部分不这样做时,你真的想要追求 RTSP/RTP 吗??

      IMO - 在 HTML5/WebRTC 的保护伞下有更好的媒体/流方法。比如看看'Ondello' 对流做了什么。

      也就是说,这里是一些使用“netty”和“efflux”的 android/RTSP/SDP/RTP 的旧项目代码。它将在 SDP 文件提供者上协商“会话”的某些部分。不记得它是否真的会播放Youtube/RTSP stuff 的音频部分,但那是我当时的目标。 (我认为它使用 AMR-NB 编解码器可以工作,但是有很多问题,我像一个坏习惯一样在 android 上放弃了 RTSP!)

      on Git....

              @Override
              public void mediaDescriptor(Client client, String descriptor)
              {
                  // searches for control: session and media arguments.
                  final String target = "control:";
                  Log.d(TAG, "Session Descriptor\n" + descriptor);
                  int position = -1;
                  while((position = descriptor.indexOf(target)) > -1)
                  {
                      descriptor = descriptor.substring(position + target.length());
                      resourceList.add(descriptor.substring(0, descriptor.indexOf('\r')));
                  }
              }
              private int nextPort()
              {
                  return (port += 2) - 2;
              }       
      
      
              private void getRTPStream(TransportHeader transport){
      
                  String[] words;
                  // only want 2000 part of 'client_port=2000-2001' in the Transport header in the response
      
                  words = transport.getParameter("client_port").substring(transport.getParameter("client_port").indexOf("=") +1).split("-");
                  port_lc = Integer.parseInt(words[0]);
      
                  words = transport.getParameter("server_port").substring(transport.getParameter("server_port").indexOf("=") +1).split("-");
                  port_rm = Integer.parseInt(words[0]);
      
                  source = transport.getParameter("source").substring(transport.getParameter("source").indexOf("=") +1);          
                  ssrc = transport.getParameter("ssrc").substring(transport.getParameter("ssrc").indexOf("=") +1);
                  // assume dynamic Packet type = RTP , 99
                  getRTPStream(session, source, port_lc, port_rm, 99);
                  //getRTPStream("sessiona", source, port_lc, port_rm, 99);
                  Log.d(TAG, "raw parms " +port_lc +" " +port_rm +" " +source );
      //          String[] words = session.split(";");
              Log.d(TAG, "session: " +session);   
              Log.d(TAG, "transport: " +transport.getParameter("client_port") 
                      +" "  +transport.getParameter("server_port") +" "  +transport.getParameter("source") 
                      +" "  +transport.getParameter("ssrc"));
      
              }
      
              private void getRTPStream(String session, String source, int portl, int portr, int payloadFormat ){
                  // what do u do with ssrc?
                  InetAddress addr;
                  try {
                      addr = InetAddress.getLocalHost();
                      // Get IP Address
       //             LAN_IP_ADDR = addr.getHostAddress();
                      LAN_IP_ADDR = "192.168.1.125";
                      Log.d(TAG, "using client IP addr " +LAN_IP_ADDR);
      
                  } catch (UnknownHostException e1) {
                      // TODO Auto-generated catch block
                      e1.printStackTrace();
                  }
      
      
                  final CountDownLatch latch = new CountDownLatch(2);
      
                  RtpParticipant local1 = RtpParticipant.createReceiver(new RtpParticipantInfo(1), LAN_IP_ADDR, portl, portl+=1);
           //       RtpParticipant local1 = RtpParticipant.createReceiver(new RtpParticipantInfo(1), "127.0.0.1", portl, portl+=1);
                  RtpParticipant remote1 = RtpParticipant.createReceiver(new RtpParticipantInfo(2), source, portr, portr+=1);
      
      
                  remote1.getInfo().setSsrc( Long.parseLong(ssrc, 16));
                  session1 = new SingleParticipantSession(session, payloadFormat, local1, remote1);
      
                 Log.d(TAG, "remote ssrc " +session1.getRemoteParticipant().getInfo().getSsrc());
      
                  session1.init();
      
                  session1.addDataListener(new RtpSessionDataListener() {
                      @Override
                      public void dataPacketReceived(RtpSession session, RtpParticipantInfo participant, DataPacket packet) {
           //               System.err.println("Session 1 received packet: " + packet + "(session: " + session.getId() + ")");
                          //TODO close the file, flush the buffer
      //                  if (_sink != null) _sink.getPackByte(packet);
                          getPackByte(packet);
      
           //             System.err.println("Ssn 1  packet seqn: typ: datasz "  +packet.getSequenceNumber()  + " " +packet.getPayloadType() +" " +packet.getDataSize());
           //             System.err.println("Ssn 1  packet sessn: typ: datasz "  + session.getId() + " " +packet.getPayloadType() +" " +packet.getDataSize());
       //                   latch.countDown();
                      }
      
                  });
           //       DataPacket packet = new DataPacket();
            //      packet.setData(new byte[]{0x45, 0x45, 0x45, 0x45});
           //       packet.setSequenceNumber(1);
           //       session1.sendDataPacket(packet);
      
      
      //        try {
             //       latch.await(2000, TimeUnit.MILLISECONDS);
           //     } catch (Exception e) {
         //         fail("Exception caught: " + e.getClass().getSimpleName() + " - " + e.getMessage());
      
       //      }
              }
       //TODO  below should collaborate with the audioTrack object and should write to the AT buffr
              // audioTrack write was blocking forever 
      
          public void getPackByte(DataPacket packet) {
                  //TODO this is getting called but not sure why only one time 
                  // or whether it is stalling in mid-exec??
      
                  //TODO on firstPacket write bytes and start audioTrack
                  // AMR-nb frames at 12.2 KB or format type 7 frames are handled . 
                  // after the normal header, the getDataArray contains extra 10 bytes of dynamic header that are bypassed by 'limit'
      
      
                  // real value for the frame separator comes in the input stream at position 1 in the data array
                  // returned by 
      
      //          int newFrameSep = 0x3c;
                  // bytes avail = packet.getDataSize() - limit;
      
      //          byte[] lbuf = new byte[packet.getDataSize()];
      //          if ( packet.getDataSize() > 0)
      //              lbuf = packet.getDataAsArray();
                  //first frame includes the 1 byte frame header whose value should be used 
                  // to write subsequent frame separators 
                  Log.d(TAG, "getPackByt start and play");
      
                  if(!started){
                      Log.d(TAG, " PLAY  audioTrak");
                      track.play();
                      started = true;
                  }
      
      //          track.write(packet.getDataAsArray(), limit, (packet.getDataSize() - limit));
                  track.write(packet.getDataAsArray(), 0, packet.getDataSize() );
                  Log.d(TAG, "getPackByt aft write");
      
      //          if(!started && nBytesRead > minBufferSize){
          //          Log.d(TAG, " PLAY  audioTrak");
              //      track.play();
              //  started = true;}
                  nBytesRead += packet.getDataSize(); 
                  if (nBytesRead % 500 < 375) Log.d(TAG, " getPackByte plus 5K received");
              }       
          }
      

      【讨论】:

        【解决方案3】:

        实际上,可以使用ExoPlayer 的修改版本在Android 上播放RTSP/RTP 流,该版本正式不支持RTSP/RTP (issue 55),但是,有一个活动的拉取请求#3854 要添加这种支持。

        同时,您可以克隆原作者exoplayer fork 支持RTSP(分支dev-v2-rtsp):

        git clone -b dev-v2-rtsp https://github.com/tresvecesseis/ExoPlayer.git.
        

        我已经对其进行了测试,并且效果很好。作者正在积极努力解决许多用户报告的问题,我希望 RTSP 支持在某个时候成为官方 exoplayer 的一部分。

        【讨论】:

          【解决方案4】:

          很遗憾,无法使用 Android MediaPlayer 播放 RTP 流。

          解决此问题的方法包括使用 ffmpeg 对 RTP Stream 进行解码。关于如何为 Android 编译 ffmpeg 的教程可以在网上找到。

          【讨论】:

          • 我在互联网上搜索了很多,有几个教程和帖子解释了如何构建它。有些工作,有些不工作。你可能想看看这个帖子roman10.net/?p=389,甚至看看RockPlayer 的人做了什么rockplayer.com/tech_en.html。第一个描述依赖于 RockPlayer 人构建的脚本。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-06-03
          • 1970-01-01
          • 2020-12-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多