Java — передача голоса через сокеты Java

Я создал серверное приложение, которое получает звук от клиента, затем я транслирую этот звук, который хранится в виде байтов, и отправляю байты обратно клиентам, подключенным к серверу. сейчас я использую только один клиент для тестирования, и клиент получает голос обратно, но звук все время заикается. Может кто-нибудь, пожалуйста, скажите мне, что я делаю неправильно?

Я думаю, что понимаю часть того, почему звук не воспроизводится плавно, но не понимаю, как решить проблему.

код ниже.

Клиент:

Часть, которая отправляет голос на сервер

     public void captureAudio()
     {


      Runnable runnable = new Runnable(){

     public void run()
     {
          first=true;
          try {
           final AudioFileFormat.Type fileType = AudioFileFormat.Type.AU;                      
           final AudioFormat format = getFormat();
           DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
           line = (TargetDataLine)AudioSystem.getLine(info);               
           line.open(format);
           line.start();
                int bufferSize = (int) format.getSampleRate()* format.getFrameSize();
                byte buffer[] = new byte[bufferSize];           

                    out = new ByteArrayOutputStream();
                    objectOutputStream = new BufferedOutputStream(socket.getOutputStream());
                    running = true;
                    try {                      
                        while (running) {                         
                            int count = line.read(buffer, 0, buffer.length);
                            if (count > 0) {
                                objectOutputStream.write(buffer, 0, count);
                                out.write(buffer, 0, count);
                                InputStream input = new ByteArrayInputStream(buffer);
                                final AudioInputStream ais = new AudioInputStream(input, format, buffer.length /format.getFrameSize());

                            }                           
                        }
                        out.close();
                        objectOutputStream.close();
                    }
                    catch (IOException e) {                    
                        System.exit(-1);
                        System.out.println("exit");
                    }
          }
          catch(LineUnavailableException e) {
            System.err.println("Line Unavailable:"+ e);
            e.printStackTrace();
            System.exit(-2);
          }
          catch (Exception e) {
           System.out.println("Direct Upload Error");
           e.printStackTrace();
          }
     }

     };

     Thread t = new Thread(runnable);
     t.start();

     }

Часть, которая получает байты данных с сервера

    private void playAudio() {
     //try{


    Runnable runner = new Runnable() {

    public void run() {
        try {
            InputStream in = socket.getInputStream();
            Thread playTread = new Thread();

            int count;
            byte[] buffer = new byte[100000];
            while((count = in.read(buffer, 0, buffer.length)) != -1) {

                PlaySentSound(buffer,playTread);
            }
        }
        catch(IOException e) {
                System.err.println("I/O problems:" + e);
                System.exit(-3);
        }
      }
    };

    Thread playThread  = new Thread(runner);
    playThread.start();
  //}
  //catch(LineUnavailableException e) {
   //System.exit(-4);
  //}
    }//End of PlayAudio method

    private void PlaySentSound(final byte buffer[], Thread playThread)
    {

    synchronized(playThread)
    {

    Runnable runnable = new Runnable(){

    public void run(){
        try
        {

                InputStream input = new ByteArrayInputStream(buffer);
                final AudioFormat format = getFormat();
                final AudioInputStream ais = new AudioInputStream(input, format, buffer.length /format.getFrameSize());
                DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
                sline = (SourceDataLine)AudioSystem.getLine(info);
                sline.open(format);
                sline.start();              
                Float audioLen = (buffer.length / format.getFrameSize()) * format.getFrameRate();

                int bufferSize = (int) format.getSampleRate() * format.getFrameSize();
                byte buffer2[] = new byte[bufferSize];
                int count2;


                ais.read( buffer2, 0, buffer2.length);
                sline.write(buffer2, 0, buffer2.length);
                sline.flush();
                sline.drain();
                sline.stop();
                sline.close();  
                buffer2 = null;


        }
        catch(IOException e)
        {

        }
        catch(LineUnavailableException e)
        {

        }
    }
    }; 
   playThread = new Thread(runnable);
   playThread.start();
   }


   }

person redoc01    schedule 11.10.2011    source источник


Ответы (2)


Вы разбиваете звуковые пакеты на куски по 1000000 байт совершенно случайным образом и воспроизводите их на стороне клиента, не принимая во внимание частоту дискретизации и размер кадра, которые вы рассчитали на стороне сервера, поэтому вы в конечном итоге разделите части звука на два, которые принадлежат друг другу. вместе.

Вам нужно декодировать те же фрагменты на сервере, которые вы отправляете на стороне клиента. Возможно, их проще отправить с помощью http multipart (где разделить данные довольно просто), а затем сделать это обычным способом через сокеты. Самый простой способ — использовать http-клиент apache commons, посмотрите здесь: http://hc.apache.org/httpclient-3.x/methods/multipartpost.html

person HefferWolf    schedule 11.10.2011
comment
спасибо за ваш ответ, это действительно помогло, я понял, что вы сказали, и изменил мой код. Что я сделал, так это отправил сегменты байтов, и после того, как полный байт сегмента закончился, я бы добавил дополнительный байт, чтобы сказать, что сегмент завершен. Теперь это работает нормально, поток намного четче, но когда я говорю, я получаю повторяющийся звук того, что я говорю. Так что, если я скажу «Привет», это снова повторит «Привет». Вы знаете, что это может быть? - person redoc01; 12.10.2011
comment
Привет, я наконец (ТАКЖЕ С ВАШЕЙ ПОМОЩЬЮ) заставил его работать, звук чистый, не идеальный, но вы можете слышать сообщение достаточно четко, просто хотел еще раз поблагодарить вас. я постараюсь настроить его, чтобы сделать голос более четким, но я более чем доволен тем, что работает. - person redoc01; 12.10.2011

В дополнение к ответу HefferWolf я бы добавил, что вы тратите много трафика, отправляя образцы звука, которые вы читаете с микрофона. Вы не говорите, ограничено ли ваше приложение локальной сетью, но если вы выходите в Интернет, обычно сжимают/распаковывают звук при отправке/получении.

Обычно используемой схемой сжатия является кодек SPEEX (доступна реализация Java здесь), который относительно прост в использовании, несмотря на то, что документация выглядит немного пугающе, если вы не знакомы с сэмплированием/сжатием аудио.

На стороне клиента вы можете использовать org.xiph.speex.SpeexEncoder для кодирования:

  • Используйте SpeexEncoder.init() для инициализации кодировщика (это должно соответствовать частоте дискретизации, количеству каналов и порядку байтов вашего AudioFormat), а затем
  • SpeexEncoder.processData() для кодирования кадра,
  • SpeexEncoder.getProcessedDataByteSize() и SpeexEncoder.getProcessedData() для получения закодированных данных

На стороне клиента используйте org.xiph.speex.SpeexDecoder для декодирования получаемых кадров:

  • SpeexDecoder.init() для инициализации декодера с использованием тех же параметров, что и кодировщик,
  • SpeexDecoder.processData() для декодирования кадра,
  • SpeexDecoder.getProcessedDataByteSize() и SpeexDecoder.getProcessedData() для получения закодированных данных

Есть еще кое-что, что я обрисовал в общих чертах. Например, вам придется выплевывать данные в правильный размер для кодирования, который зависит от частоты дискретизации, каналов и битов на сэмпл, но вы увидите резкое уменьшение количества байтов, которые вы отправляете по сети. сеть.

person edoloughlin    schedule 30.03.2012