Java - 通过Java sockets广播语音

5
我已经创建了一个服务器应用程序,可以接收客户端发送的声音,然后将其作为字节存储并将字节发送回连接到服务器的客户端。目前我仅在测试中使用一个客户端,该客户端可以接收声音,但声音一直在打断。请问是否有人能告诉我问题出在哪里?
我认为我理解了声音无法流畅播放的部分原因,但不知道如何解决这个问题。
以下是代码:
客户端:
发送声音到服务器的部分:
     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();
   }


   }
2个回答

5
除了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()获取已编码数据。
还有一些细节需要处理。例如,您需要将数据分成正确的大小以进行编码,这取决于采样率、通道和每个样本的位数,但您会看到发送到网络上的字节数量显著减少。

4
你将声音包随意地分成了1000000字节的片段,并在客户端上播放它们,而没有考虑在服务器端计算的采样率和帧大小,因此你最终会将声音的片段分成两个本应该合并的部分。
你需要在服务器上解码与客户端发送的相同的数据块。也许使用http multipart(其中拆分数据非常容易)比通过套接字基本方式更容易。最简单的方法是使用apache commons http client,可以在这里查看:http://hc.apache.org/httpclient-3.x/methods/multipartpost.html

谢谢您的回复,它真的帮了我很多。我理解了您所说的并修改了我的代码。我所做的是发送字节段,并在完整的字节段结束后添加一个额外的字节来表示该段已经完成。现在这个流程运行得很好,但当我说话时,会出现重复的声音。例如,如果我说“你好”,那么它会再次重复“你好”。您知道这可能是什么原因吗? - redoc01
嗨,最终(也得感谢你的帮助)我终于让它工作了,声音很清晰,虽然不是完美的,但你可以听到足够清晰的信息,只是想再次感谢你。我会尝试调整它,使声音更加清晰,但我对目前的工作状态已经非常满意了。 - redoc01

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接