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

8
我正在尝试创建一个Speex Voip客户端和服务器。我已经掌握了基础知识,并且在本地机器上使用UDP运行得很好。我正在使用JSpeex以实现可移植性。我想寻求有关创建客户端和服务器的提示。您有什么想法吗?
JSpeex库每次只能编码320字节,因此发送到服务器的数据包很小(在我的情况下约为244字节)。客户端等到大约1或2KB的编码数据准备就绪再发送是否更好,还是让服务器处理缓冲数据包?
此外,如何实现数据缓冲方面的帮助将是不错的。
这是一些在本地机器上运行的内容。
客户端:
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();
        if (nAvailable >= nBytesToRead) {
            int nBytesRead = m_line.read(abPCMData, 0, nBytesToRead);
            if (nBytesRead == -1) break;
            if (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);
            try {
                m_connection.send(packet);
            }
            catch(IOException iox) {
                System.out.println("Connection to server lost: " + iox.getMessage());
                break;
            }
        }
    }
    closeLine();
    disconnect();
}

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);
    return abEncodedData;
}

服务器:

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

    while (m_bServerRunning) {
        try {
            m_serverSocket.receive(packet);
            //System.out.println("Packet size is " + packet.getData().length);
            //System.out.println("Got packet from " + packet.getAddress().getHostAddress());
            //abPCMData = decodeSpeexPacket(packet.getData(),  0, packet.getLength());
            UserSpeexPacket usp = UserSpeexPacket.datagramPacketToUserSpeexPacket(packet);
            abPCMData = decodeSpeexPacket(usp.getSpeexData(), 0, usp.getSpeexData().length);
            m_srcDataLine.write(abPCMData, 0, abPCMData.length);

            if (System.currentTimeMillis() >= (lPrevVolPrintTime + 500)) {
                //System.out.println("Current volume: " + AudioUtil.getVolumeLevelForPCM22050Hz16Bit1Channel(abPCMData, 0, abPCMData.length));
                lPrevVolPrintTime = System.currentTimeMillis();
            }
        }
        catch (IOException iox) {
            if (m_bServerRunning) {
                System.out.println("Server socket broke: " + iox.getMessage());
                stopServer();
            }
        }
    }

1个回答

5
我正在处理一个类似的项目。根据我所读到的和个人经验,你最好的选择是使用小数据块,并尽快发送它们。你希望任何抖动缓冲都在接收方完成。
典型的VoIP应用程序每秒发送50-100个数据包。对于8000Hz的uLaw编码,这将导致数据包大小为80-160字节。原因在于某些数据包不可避免地会丢失,而你希望接收方的影响尽可能小。因此,每个数据包的音频数据持续时间为10ms或20ms,一个丢失的数据包可能会导致短暂的中断,但远不及丢失2k的音频数据(约250ms)那么严重。
此外,如果数据包大小很大,则必须在发送方累积所有数据后才能发送。因此,考虑到典型的网络延迟为50ms,每个数据包有20ms的音频数据,接收方至少需要等待70ms才能听到发送方说的话。现在想象一下当一次发送250ms的音频时会发生什么。发送者说话和接收者播放音频之间将经过270ms。
用户似乎更容易原谅偶尔的数据包丢失,从而导致次优的音频质量,因为大多数电话的音频质量本来就不是很好。但是,用户习惯了现代电话线路上非常低的延迟,因此即使引入250ms的往返延迟也可能极其令人沮丧。
现在,关于实现缓冲,我发现一个好的策略是使用队列(哎呀,这里使用.NET :)),然后将其包装在一个类中,跟踪队列中所需的最小和最大数据包数。使用严格的锁定,因为你很可能会从多个线程访问它。如果队列“底部”并且其中没有任何数据包(缓冲区欠流),则设置标志并返回null,直到数据包计数达到所需的最小值。你的使用者必须检查是否返回null,并且不将任何东西排队到输出缓冲区中。或者,你的使用者可以跟踪最后一个数据包并重复将其排队,这可能会导致循环音频,但在某些情况下,这可能比静音“听起来”更好。你必须一直这样做,直到生产者将足够的数据包放入队列以达到最小值。这将导致用户长时间的静音,但通常比短暂而频繁的静音(卡顿)更容易被接受。如果你获得一组数据包并且生产者填充了队列(达到所需的最大值),则可以开始忽略新的数据包,或者从队列前面删除足够数量的数据包以返回到最小值。
选择这些最小/最大值很困难。你正在试图平衡平滑的音频(没有欠流)和发送方和接收方之间的最小延迟。VoIP很有趣,但肯定会让人沮丧!祝你好运!

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