Java语音聊天滞后

时间:2016-04-30 02:15:21

标签: java audio voice-recording javax.sound.sampled

我正在制作一个语音聊天/信使程序,我在聊天中与一个人合作,但是当我添加一秒钟时,声音很迟钝而且被切断了。我认为问题出在Client Audio Receive类中。如果您认为不是,我会将其余内容链接到一个pastebin中。

package client;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;

public class ClientAudioRec implements Runnable {
private ObjectInputStream i2;
private Socket s;
private AudioFormat af;

public ClientAudioRec(Socket s2, AudioFormat audioformat) {
    s = s2;
    af = audioformat;
}
public void run() {
    try {
        i2 = new ObjectInputStream(s.getInputStream());
    } catch (IOException e2) {
        e2.printStackTrace();
    }

    SourceDataLine inSpeaker = null;
    DataLine.Info info = new DataLine.Info(SourceDataLine.class, af);
    try {
        inSpeaker = (SourceDataLine)AudioSystem.getLine(info);
        inSpeaker.open(af);
    } catch (LineUnavailableException e1) {
        e1.printStackTrace();
    }

    int bytesRead = 0;
    byte[] inSound = new byte[100];
    inSpeaker.start();
    while(true)
    {
        try{
            bytesRead = i2.read(inSound, 0, inSound.length);
            } catch (Exception e){
            e.printStackTrace();
        }
        if(bytesRead >= 0)
        {
            inSpeaker.write(inSound, 0, bytesRead);
        }
    }
 }

}

Server Side Voice Code HERE

Client Side Audio Input Code HERE

1 个答案:

答案 0 :(得分:1)

我怀疑您的服务器端语音代码:byte[] soundData = new byte[1];。一个字节的缓冲区?你能让CPU更难工作吗?哦,你也可以在你的客户端音频输入代码中这样做。

发送语音的数据速率是多少?手机使用20ms帧。这些是完全采样(20ms),然后发送到基站(20ms),并可能发送到另一个手机(20ms),然后最后通过扬声器播放至少60ms的延迟。听到没有不自然的延迟。手机数据速率为8kbps,因此每帧为160位或20字节。我将缓冲区大小增加到至少20个字节(可能高达50个字节),看看你是否有任何改进。

套接字"服务质量"设置会影响性能。对于VoIP,您需要低延迟连接。我不确定如何为Java套接字设置它;我不得不做一些阅读。 TCP_NODELAY是设置(如果可能)的另一个选项,以防止延迟确认减慢后续数据包。发生这种情况时会发送许多小数据包。发送更大的数据包将缓解这种情况,这是将缓冲区增加到1个字节以上的另一个原因!

修改

您应该将数据累积到更大的固定大小的帧(例如20ms的数据),而不是发送许多微小的缓冲区,而只发送完整的帧。要将数据累积到帧缓冲区,请使用#read(byte[] buffer, int offset, int length)方法。例如:

byte[] buffer = new byte[100];
int offset = 0;

while(true) {
    // Read as many bytes as possible, up to remaining space in buffer
    int bytes_read = source.read(buffer, offset, buffer.length - offset);

    if (bytes_read >= 0) {
        // Accumulate number of bytes that has been read.
        offset += bytes_read;

        if (offset == buffer.length) {
            // Buffer is full, send it.
            sink.write(buffer, 0, buffer.length);

            // Clear buffer for next frame 
            offset = 0;
        }
    } else {
        break; // End of stream
    }
}

如果读取30个字节,则将它们读入offset=0的缓冲区,并将offset递增为30.如果在下一遍中读取60个以上的字节,则将它们读入缓冲区从offset=30开始,offset递增到90.如果之后有50个字节可用,则只读取10个字节(buffer.length-offset)填充缓冲区。然后发送缓冲区,offset重置为零。剩余的40个字节(或者更多,因为数据保持到达)将在下一次调用时被读取。

注意:如果在一次调用中无法将整个缓冲区写入套接字,则应在sink.write()周围使用类似的循环。