Задержка голосового чата Java

Я делаю программу голосового чата / мессенджера, и у меня есть голос для работы с одним человеком в чате, но когда я добавляю в него секунду, голоса становятся запаздывающими и сокращаются. Я думаю, что проблема в классе Client Audio Receive. Если вы не думаете, что это так, я свяжу все остальное в пастбине.

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

}

Голосовой код на стороне сервера ЗДЕСЬ

Код аудиовхода на стороне клиента ЗДЕСЬ

1 ответ

Решение

Я бы с подозрением отнесся к вашему коду на стороне сервера: byte[] soundData = new byte[1];, Однобайтовый буфер? Можете ли вы заставить процессор работать тяжелее? О, и вы делаете это в вашем коде аудио входа на стороне клиента тоже.

Какова скорость передачи данных вашего голоса? Сотовые телефоны используют кадры 20 мс. Они полностью дискретизируются (20 мс), затем передаются на базовую станцию ​​(20 мс) и, возможно, отправляются на другой сотовый телефон (20 мс), а затем, наконец, воспроизводятся через динамик с задержкой не менее 60 мс. Никакой неестественной задержки не слышно. Скорость передачи данных сотового телефона составляет 8 Кбит / с, поэтому каждый кадр составляет 160 бит или 20 байтов. Я бы увеличил размер вашего буфера по крайней мере до 20 байт (возможно, до 50) и посмотрел бы, есть ли у вас какие-либо улучшения.

Параметр "Качество обслуживания" сокета может повлиять на производительность. Для VoIP вы хотите соединение с низкой задержкой. Я не уверен, как установить это для Java Sockets; Мне придется немного почитать. TCP_NODELAY это еще одна опция, чтобы установить (если это возможно), чтобы предотвратить задержку подтверждений, замедляющих последующие пакеты. Это происходит со многими небольшими отправляемыми пакетами. Отправка больших пакетов уменьшит это, что является еще одной причиной для увеличения вашего буфера до более чем 1 байта!


редактировать

Вместо того, чтобы отправлять множество крошечных буферов, вы должны накапливать данные в большие кадры фиксированного размера (например, 20 мс данных) и отправлять только полные кадры. Чтобы накапливать данные в буфер кадра, вы используете #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()В случае, если весь буфер не может быть записан в сокет за один вызов.

Другие вопросы по тегам