我正在制作语音聊天/信使程序,并且在聊天中可以与一个人一起工作,但是当我添加第二个声音时,声音就会变得迟钝并被割断。我认为问题出在“客户端音频接收”类中。如果您不认为是这样,我会将其余的内容链接到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);
}
}
}
}
我会怀疑您的服务器端语音代码:byte[] soundData = new byte[1];
。一个字节的缓冲区?您可以使CPU的工作更加艰苦吗?哦,您也可以在客户端音频输入代码中执行此操作。
您的发送语音的数据速率是多少?手机使用20毫秒帧。这些被完全采样(20毫秒),然后发送到基站(20毫秒),并有可能发送到另一个手机(20毫秒),最后通过扬声器播放至少60毫秒的延迟。没有听到不自然的延迟。手机数据速率为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()
,以防在一次调用中无法将整个缓冲区写入套接字。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句