2016-04-30 1 views
0

Je suis en train de créer un programme de chat/messagerie vocale et j'ai de la voix pour travailler avec une personne dans le chat, mais quand j'y ajoute une seconde, les voix sont décalées et coupées. Je pense que le problème est dans la classe Client Audio Receive. Si vous ne le pensez pas, je lierai le reste dans un pastebin.Java Voice Chat Lag

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

} 

Server Side Voice Code HERE

Client Side Audio Input Code HERE

+0

Pourriez-vous résoudre le problème plus? –

+0

non je travaillais dessus pendant quelques jours avant cela. –

Répondre

1

Je serais méfiant de votre code côté serveur vocal: byte[] soundData = new byte[1];. Un tampon d'un octet? Pouvez-vous faire fonctionner le processeur plus dur? Oh, et vous le faites aussi dans votre code d'entrée audio côté client.

Quel est le débit de transmission de votre voix? Les téléphones cellulaires utilisent des cadres de 20 ms. Ceux-ci sont complètement échantillonnés (20ms), puis transmis à la station de base (20ms), et éventuellement envoyés à un autre téléphone portable (20ms), puis finalement lus par le haut-parleur pendant un délai d'au moins 60ms. Aucun retard artificiel n'est entendu. Le débit de données du téléphone portable est de 8 kbps, donc chaque trame est de 160 bits, ou 20 octets. J'augmenterais la taille de votre tampon à au moins 20 octets (peut-être jusqu'à 50) et verra si vous obtenez une amélioration.

Le paramètre "Qualité de service" du support peut affecter les performances. Pour la VoIP, vous voulez une connexion à faible latence. Je ne suis pas certain comment régler cela pour Java Sockets; Je vais devoir lire. TCP_NODELAY est une autre option à définir (si possible) pour empêcher les accusés de réception retardés de ralentir les paquets suivants. Cela arrive avec de nombreux petits paquets envoyés. L'envoi de plus gros paquets atténuera ceci, ce qui est une autre raison d'augmenter votre tampon à plus d'un octet!


Modifier

Au lieu d'envoyer de nombreux tampons minuscules, vous devez accumuler des données dans des trames de taille fixe plus importants (tels que 20ms de données), et seulement envoyer des trames complètes. Pour accumuler des données dans un tampon de trame, vous utilisez la méthode #read(byte[] buffer, int offset, int length). Par exemple:

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

Si 30 octets sont lus, ils sont lus dans le tampon à offset=0 et offset est incrémentée à 30. Si 60 plus les octets sont lus dans le passage suivant, ils sont lus dans le départ tampon à offset=30, et offset est incrémenté à 90. Si 50 octets deviennent disponibles après cela, seulement 10 octets seront lus (buffer.length-offset) en remplissant le tampon. Le tampon est ensuite envoyé et offset est remis à zéro. Les 40 octets restants (ou peut-être plus, puisque les données continuent d'arriver) seront lus lors de l'appel suivant.

Remarque: vous devez utiliser une boucle similaire autour de sink.write(), au cas où le tampon entier ne peut pas être écrit dans le socket en un seul appel.

+0

je l'ai mis à 50 mais il est toujours aussi saccadé –

+0

Au lieu de lire et envoyer des tampons de moins de 50 octets sur le socket, essayez d'accumuler jusqu'à ce que le tampon est plein, et seulement envoyer des tampons ** full **. Augmentez ensuite la taille de la mémoire tampon. Peut-être attendre jusqu'à ce que vous ayez 5 secondes d'audio tamponné sur le récepteur avant de commencer à le jouer; cela devrait éviter tout coup de bâton. Obtenez ce débogué et de travail, puis diminuez la taille de la mémoire tampon à 3 secondes, 1 seconde, une demi-seconde, et voir où la côtelette revient. – AJNeufeld

+0

comment l'enregistrez-vous puis envoyez-le? –