Помогите с созданием Speex Voip сервера и клиента - PullRequest
8 голосов
/ 27 сентября 2010

Я пытаюсь создать клиент и сервер Speex Voip.У меня есть основы и работает нормально на локальной машине через UDP.Я использую JSpeex для мобильности.Я ищу советы по созданию клиента и сервера.Что вы думаете?

Библиотека JSpeex может кодировать только 320 байтов за вызов, поэтому пакеты, отправляемые на сервер, являются крошечными (в моем случае ~ 244 байта).Было бы лучше для клиента подождать, пока не будет готово около 1 или 2 КБ закодированных данных, прежде чем отправлять или позволить серверу обрабатывать буферизацию пакетов?

Кроме того, любая помощь в том, как реализовать буферизацию данных, будетприятно.

Часть того, что у меня есть, работает на локальной машине.

Клиент:

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 голосов
/ 14 октября 2010

Я работаю над аналогичным проектом. Исходя из всего, что я прочитал, и из личного опыта, ваш лучший вариант - работать с небольшими кусочками данных и отправлять их как можно скорее. Вы хотите, чтобы любая буферизация дрожания выполнялась на стороне приемника.

Для приложения VoIP характерно отправлять 50-100 пакетов в секунду. Для кодирования uLaw на 8000 Гц это привело бы к размеру пакета 80-160 байт. Это объясняется тем, что некоторые пакеты неизбежно будут отброшены, и вы хотите, чтобы влияние на получателя было как можно меньше. Таким образом, при 10 мс или 20 мс аудиоданных на пакет упавший пакет может привести к небольшому сбоям, но не так плохо, как потеря 2 тыс. Аудиоданных (~ 250 мс).

Кроме того, при большом размере пакета необходимо накапливать все данные у отправителя перед его отправкой. Таким образом, учитывая типичную задержку в сети 50 мс при 20 мс аудиоданных на пакет, получатель не будет слышать то, что говорит отправитель, как минимум 70 мс. А теперь представьте, что происходит, когда одновременно отправляется 250 мс звука. 270 мсек пройдет между отправителем, говорящим и получателем, воспроизводящим этот звук.

Пользователи, кажется, более и более снисходительны к потере пакетов здесь и там, что приводит к качеству звука ниже номинального, потому что качество звука большинства телефонов не так уж велико с самого начала. Тем не менее, пользователи также привыкли к очень низкой задержке в современных телефонных сетях, поэтому введение задержки туда и обратно даже в 250 мс может быть крайне неприятным.

Теперь, что касается реализации буферизации, я нашел хорошую стратегию, чтобы использовать Очередь (упс, используя .NET здесь :)), а затем обернуть это в класс, который отслеживает желаемое минимальное и максимальное количество пакетов в очередь. Используйте строгую блокировку, поскольку вы, скорее всего, будете получать к ней доступ из нескольких потоков. Если очередь «выходит из строя» и содержит нулевые пакеты (переполнение буфера), установите флаг и возвращайте ноль, пока количество пакетов не достигнет желаемого минимума. Однако ваш потребитель должен будет проверить, возвращено ли значение null, и ничего не помещать в буфер вывода. В качестве альтернативы ваш потребитель может отслеживать последний пакет и повторно ставить его в очередь, что может вызвать зацикливание звука, но в некоторых случаях это может «звучать» лучше, чем тишина. Вам придется делать это до тех пор, пока производитель не поместит достаточно пакетов в очередь, чтобы достичь минимума. Это приведет к более длительному периоду молчания для пользователя, но это обычно лучше, чем короткие, частые периоды молчания (прерывистость). Если вы получаете пакет пакетов, и производитель заполняет очередь (достигая желаемого максимума), вы можете либо начать игнорировать новые пакеты, либо отбросить достаточно пакетов с начала очереди, чтобы вернуться к минимуму.

Трудно выбрать эти минимальные / максимальные значения. Вы пытаетесь сбалансировать плавный звук (без потерь) с минимальной задержкой между отправителем и получателем. VoIP это весело, но это может быть разочарование! Удачи!

...