gpt4 book ai didi

java - 帮助创建 Speex Voip 服务器和客户端

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:52:23 25 4
gpt4 key购买 nike

我正在尝试创建 Speex Voip 客户端和服务器。我掌握了基础知识,并且它通过 UDP 在本地机器上工作正常。我正在使用 JSpeex 来实现可移植性。我正在寻找有关创建客户端和服务器的技巧。你有什么想法?

JSpeex 库每次调用只能编码 320 个字节,因此发送到服务器的数据包很小(在我的例子中约为 244 个字节)。客户端等待大约 1 或 2 KB 的编码数据在发送之前准备好或让服务器处理缓冲数据包会更好吗?

此外,任何关于如何实现缓冲数据的帮助都会很好。

我拥有的一些可以在本地机器上运行的东西。

客户:



public void run() {
int nBytesToRead = (m_inputAudioFormat.getFrameSize() * 160);
int nAvailable = 0;
byte[] abPCMData = new byte[nBytesToRead];
byte[] abSpeexData = null;
UserSpeexPacket userSpeexPacket = new UserSpeexPacket("Xiphias3", "TheLounge", null, 0);

while (m_captureThread != null) {
nAvailable = m_line.available();
如果(nAvailable >= nBytesToRead){
int nBytesRead = m_line.read(abPCMData, 0, nBytesToRead);
如果(nBytesRead == -1)中断;
如果(nBytesRead < nBytesToRead)
Arrays.fill(abPCMData, nBytesRead, abPCMData.length, (byte) 0);
abSpeexData = createSpeexPacketFromPCM(abPCMData, 0, abPCMData.length);
//DatagramPacket packet = new DatagramPacket(abSpeexData, 0, abSpeexData.length, m_connection.getInetAddress(), m_nServerPort);
userSpeexPacket.setSpeexData(abSpeexData);
userSpeexPacket.incrementPacketNumber();
DatagramPacket packet = UserSpeexPacket.userSpeexPacketToDatagramPacket(m_connection.getInetAddress(), m_connection.getPort(), userSpeexPacket);
尝试 {
m_connection.send(数据包);
}
catch (IOException iox){
System.out.println("与服务器的连接丢失:"+ iox.getMessage());
休息;
}
}
}
关闭线();
断开();
}

public byte[] createSpeexPacketFromPCM(byte[] abPCMData, int nOffset, int nLength)
{
byte[] abEncodedData = null;
m_speexEncoder.processData(abPCMData, nOffset, nLength);
abEncodedData = new byte[m_speexEncoder.getProcessedDataByteSize()];
m_speexEncoder.getProcessedData(abEncodedData, 0);
返回 abEncodedData;
}

服务器:



 DatagramPacket packet = new DatagramPacket(new byte[2048], 0, 2048);
byte[] abPCMData = null;
long lPrevVolPrintTime = 0;

而(m_bServerRunning){
尝试 {
m_serverSocket.receive(数据包);
//System.out.println("数据包大小为"+ packet.getData().length);
//System.out.println("Got packet from "+ packet.getAddress().getHostAddress());
//abPCMData = decodeSpeexPacket(packet.getData(), 0, packet.getLength());
UserSpeexPacket usp = UserSpeexPacket.datagramPacketToUserSpeexPacket(数据包);
abPCMData = decodeSpeexPacket(usp.getSpeexData(), 0, usp.getSpeexData().length);
m_srcDataLine.write(abPCMData, 0, abPCMData.length);

如果 (System.currentTimeMillis() >= (lPrevVolPrintTime + 500)) {
//System.out.println("当前音量:"+ AudioUtil.getVolumeLevelForPCM22050Hz16Bit1Channel(abPCMData, 0, abPCMData.length));
lPrevVolPrintTime = System.currentTimeMillis();
}
}
catch (IOException iox){
如果(m_bServerRunning){
System.out.println("服务器套接字中断:"+ iox.getMessage());
停止服务器();
}
}
}

最佳答案

我正在做一个类似的项目。从我读过的所有内容和个人经验来看,您最好的选择是使用少量数据并尽快发送它们。您希望在接收器端完成任何抖动缓冲。

VoIP 应用程序通常每秒发送 50-100 个数据包。对于 8000Hz 的 uLaw 编码,这将导致数据包大小为 80-160 字节。这样做的原因是一些数据包不可避免地会被丢弃,而您希望对接收方的影响尽可能小。因此,对于每个数据包 10 毫秒或 20 毫秒的音频数据,丢失的数据包可能会导致小问题,但远没有丢失 2k 音频数据(~250 毫秒)那么严重。

此外,对于较大的数据包,您必须在发送之前在发送方收集所有数据。因此,给定一个典型的 50 毫秒网络延迟,每个数据包有 20 毫秒的音频数据,接收方至少在 70 毫秒内不会听到发送方所说的内容。现在想象一次发送 250 毫秒的音频时会发生什么。发送方说话和接收方播放该音频之间将经过 270 毫秒。

用户似乎更能容忍某些地方的数据包丢失,这会导致音频质量低于标准,因为大多数电话的音频质量本来就不是很好。然而,用户也习惯于现代电话电路的极低延迟,因此即使引入 250 毫秒的往返延迟也会令人非常沮丧。

现在,就实现缓冲而言,我找到了一个使用队列的好策略(糟糕,这里使用 .NET :)),然后将其包装在一个类中,该类跟踪所需的最小和最大数据包数队列。使用严格的锁定,因为您很可能会从多个线程访问它。如果队列“触底”并且其中有零个数据包(缓冲区欠载),请设置一个标志并返回空值,直到数据包计数达到您想要的最小值。但是,您的消费者将必须检查是否返回 null 并且不将任何内容排队到输出缓冲区中。或者,您的消费者可以跟踪最后一个数据包并重复将其排队,这可能会导致循环音频,但在某些情况下,这可能“听起来”比静音更好。您将不得不这样做,直到生产者将足够的数据包放入队列以达到最小值。这将为用户带来更长的静默期,但这通常比短而频繁的静默期(断断续续)更容易接受。如果您收到大量数据包并且生产者填满了队列(达到所需的最大值),您可以开始忽略新数据包,或者从队列前面丢弃足够多的数据包以返回到最小值。

虽然选择这些最小/最大值很困难。您正在尝试平衡流畅的音频(无欠载)与发送方和接收方之间的最小延迟。 VoIP 很有趣,但它确实令人沮丧!祝你好运!

关于java - 帮助创建 Speex Voip 服务器和客户端,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3799795/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com