RTP на Android MediaPlayer

Я реализовал RTSP на Android MediaPlayer, используя VLC как 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 

Кто-нибудь знает, где проблема? Я ошибаюсь, или это невозможно проигрывать RTP на MediaPlayer? ура Джорджио

Ответ 1

У меня есть частичное решение для вас.

В настоящее время я работаю над проектом Ra & D, включающим потоки RTP медиа с сервера на Android-клиентов.

Выполняя эту работу, я вношу свой вклад в свою собственную библиотеку под названием smpte2022lib, которую вы можете найти здесь: http://sourceforge.net/projects/smpte-2022lib/.

Помогает с такой библиотекой (в настоящее время самая лучшая реализация Java) вы можете разбирать многоадресные потоки RTP, исходящие из профессиональных потоковых устройств, сеансов VLC RTP,...

Я уже успешно протестировал его с потоками, поступающими из захваченных профильных потоков RTP с помощью SMPTE-2022 2D-FEC или с простыми потоками, сгенерированными с помощью VLC.

К сожалению, я не могу поместить здесь фрагмент кода, поскольку его использование на самом деле находится под защитой авторских прав, но я гарантирую, что вы можете использовать его просто, анализируя потоки UDP, связанные с конструктором RtpPacket.

Если пакеты являются действительными RTP-пакетами (байтами), они будут декодированы как таковые.

В этот момент времени я переношу вызов конструктора RtpPacket в поток, который фактически хранит декодированную полезную нагрузку в качестве медиафайла. Затем я вызову VideoView с этим файлом в качестве параметра.

Пересечение пальцев; -)

С уважением,

Дэвид Фишер

Ответ 2

Возможно, в android, используя (а не mediaPlayer, но другие вещи дальше по стеку), но действительно ли вы хотите продолжить RTSP/RTP, когда остальная медиасистема не работает?

IMO - есть намного лучшие медиа/потоковые подходы под эгидой HTML5/WebRTC. Например, посмотрите, что 'Ondello' делает с потоками.

Тем не менее, здесь приведен код старого проекта для android/RTSP/SDP/RTP с использованием "netty" и "efflux". Он будет обсуждать некоторые части "Сессии" на поставщиков файлов SDP. Не могу вспомнить, будет ли он фактически воспроизводить звуковую часть Youtube/RTSP, но это была моя цель в то время. (я думаю, что он работал с использованием кодека AMR-NB, но было много проблем, и я сбросил RTSP на Android как плохая привычка!)

на 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

На самом деле можно воспроизводить потоки RTSP/RTP на Android с помощью модифицированной версии ExoPlayer, которая официально не поддерживает RTSP/RTP (, выпуск 55), однако существует активный запрос на извлечение. # 3854, чтобы добавить эту поддержку.

Тем временем вы можете клонировать оригинального автора exoplayer fork, который поддерживает RTSP (ветка dev-v2-rtsp):

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

Я проверил это, и он отлично работает. Авторы активно работают над устранением проблем, о которых сообщают многие пользователи, и я надеюсь, что поддержка RTSP в какой-то момент станет частью официального exoplayer.

Ответ 4

К сожалению, невозможно воспроизвести RTP-поток с помощью Android MediaPlayer.

Решение этих проблем включает в себя декодирование потока RTP с помощью ffmpeg. Учебники по сборке ffmpeg для Android можно найти в Интернете.