我正在制作一个语音聊天/信使程序,我在聊天中与一个人合作,但是当我添加一秒钟时,声音很迟钝而且被切断了。我认为问题出在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);
}
}
}
}
答案 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()
周围使用类似的循环。