2016-06-23 17 views
1

Als ein Hobby-Projekt, schreibe ich einen androiden VoIP-Client. Beim Schreiben von Sprachdaten in den Socket (Vars.mediaSocket) werden die Daten oft nicht sofort über das Wifi gesendet, sondern bleiben stehen und auf einmal sendet es 20 Sekunden lang Sprache. Dann wird es wieder stehen bleiben und 30 Sekunden warten und dann 30 Sekunden Stimme senden. Das Warten ist nicht konsistent, aber nach einer Weile wird es fortlaufend Sprachdaten senden. Ich habe alles ausprobiert, von DataOutputStream zu dem Festlegen der Socket-Ausgabepuffergröße, zu dem Festlegen der sendbuffer-Größe riesig, klein und zuletzt zu dem Puffern der Sprachdaten aus seinen 32 Byte-Chunks zu allem von 128 Byte zu 32 KB.Force Java Android Socket zum Senden von Daten sofort

 Utils.logcat(Const.LOGD, encTag, "MediaCodec encoder thread has started"); 
     isEncoding = true; 
     byte[] amrbuffer = new byte[32]; 
     short[] wavbuffer = new short[160]; 
     int outputCounter = 0; 

     //setup the wave audio recorder. since it is released and restarted, it needs to be setup here and not onCreate 
     wavRecorder = null; //remove pointer to the old recorder for safety 
     wavRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLESWAV, AudioFormat.CHANNEL_IN_MONO, FORMAT, 160); 
     wavRecorder.startRecording(); 
     AmrEncoder.init(0); 

     while(!micMute) 
     { 
      int totalRead = 0, dataRead; 
      while(totalRead < 160) 
      {//although unlikely to be necessary, buffer the mic input 
       dataRead = wavRecorder.read(wavbuffer, totalRead, 160 - totalRead); 
       totalRead = totalRead + dataRead; 
      } 
      int encodeLength = AmrEncoder.encode(AmrEncoder.Mode.MR122.ordinal(), wavbuffer, amrbuffer); 

      try 
      { 
       Vars.mediaSocket.getOutputStream().write(amrbuffer); 
       Vars.mediaSocket.getOutputStream().flush(); 
      } 
      catch (IOException i) 
      { 
       Utils.logcat(Const.LOGE, encTag, "Cannot send amr out the media socket"); 
       Utils.dumpException(tag, i); 
      } 

Gibt es etwas, das mir fehlt? Um ein zweites Handy zu simulieren, habe ich einen anderen Client, der einfach die Sprachdaten liest, wegwirft und wieder in einer Schleife liest. Ich kann in dem simulierten zweiten Handy bestätigen, wenn das reale Handy aufhört, Sprache zu senden, der simulierte socket.read hängt, bis der echte anfängt, Stimme wieder zu senden.

Ich hoffe wirklich nicht, ein jni für die Steckdose zu schreiben, da ich nichts darüber weiß und hoffte, ich könnte die App als eine Standard-Java-App schreiben.

CASE GESCHLOSSEN: Es stellte sich heraus, dass es sich um einen Server-Side-Bug handelte, aber die vereinfachten Zurück-zur-Grundlagen-Vorschläge sind immer noch eine gute Idee.

+0

Nicht innerhalb der Schleifen spülen. Wenn es keine Pufferung gibt, ist es sinnlos (wie in diesem Fall), und wenn es Pufferung gibt, zerstört es es. – EJP

Antwort

1

Sie fügen die meisten Latenzen selbst hinzu, indem Sie große Datenmengen lesen, bevor Sie eine davon schreiben. Sie sollten einfach die Standard-Java-Kopierschleife verwenden:

Sie müssen dies anpassen, um Ihren Codec Schritt zu integrieren. Beachten Sie, dass Sie keinen Puffer der Größe der gesamten Eingabe benötigen. Sie können die Größe auf sich selbst abstimmen, aber 8192 ist ein guter Ausgangspunkt. Sie können es erhöhen, um 32k zu sagen, aber verringern Sie es nicht. Wenn Ihr Codec die Daten in Blöcken fester Größe benötigt, verwenden Sie einen Puffer dieser Größe und DataInputStream.readFully(). Je größer der Puffer, desto größer die Latenz.

EDIT Spezifische Probleme mit Ihrem Code:

byte[] amrbuffer = new byte[AMRBUFFERSIZE]; 
byte[] outputbuffer = new byte [outputBufferSize]; 

entfernen (siehe unten).

short[] wavbuffer = new short[WAVBUFFERSIZE]; 
int outputCounter = 0; 

Entfernen outputCounter.

 //setup the wave audio recorder. since it is released and restarted, it needs to be setup here and not onCreate 
     wavRecorder = null; //remove pointer to the old recorder for safety 

Sinnlos. Löschen.

 wavRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLESWAV, AudioFormat.CHANNEL_IN_MONO, FORMAT, WAVBUFFERSIZE); 
     wavRecorder.startRecording(); 
     AmrEncoder.init(0); 

OK.

 try 
     { 
      Vars.mediaSocket.setSendBufferSize(outputBufferSize); 
     } 
     catch (SocketException e) 
     { 
      e.printStackTrace(); 
     } 

Sinnlos. Löschen. Der Socket-Sendepuffer sollte so groß wie möglich sein. Wenn Sie nicht wissen, dass die Standardgröße < outputBufferSize ist, gibt es keinen Vorteil. In jedem Fall werden wir outputBuffer zusammen loswerden.

 while(!micMute) 
     { 
      int totalRead = 0, dataRead; 
      while(totalRead < WAVBUFFERSIZE) 
      {//although unlikely to be necessary, buffer the mic input 
       dataRead = wavRecorder.read(wavbuffer, totalRead, WAVBUFFERSIZE - totalRead); 
       totalRead = totalRead + dataRead; 
      } 
      int encodeLength = AmrEncoder.encode(AmrEncoder.Mode.MR122.ordinal(), wavbuffer, amrbuffer); 

OK.

  if(outputCounter == outputBufferSize) 
      { 
       Utils.logcat(Const.LOGD, encTag, "Sending output buffer"); 
       try 
       { 
        Vars.mediaSocket.getOutputStream().write(outputbuffer); 
        Vars.mediaSocket.getOutputStream().flush(); 
       } 
       catch (IOException i) 
       { 
        Utils.logcat(Const.LOGE, encTag, "Cannot send amr out the media socket"); 
        Utils.dumpException(tag, i); 
       } 
       outputCounter = 0; 
      } 
      System.arraycopy(amrbuffer, 0, outputbuffer, outputCounter, encodeLength); 
      outputCounter = outputCounter + encodeLength; 
      Utils.logcat(Const.LOGD, encTag, "Output buffer fill: " + outputCounter); 

entfernen die alle oben und ersetzen

 Vars.mediaSocket.getOutputStream().write(amrbuffer, 0, encodeLength); 

Dies bedeutet auch, Sie loswerden ‚Output‘, wie versprochen zu bekommen.

NB Nicht innerhalb der Schleifen spülen. In der Tat macht das Leeren eines Socket-Ausgangsstroms nichts, aber das allgemeine Prinzip gilt immer noch.

+0

Ich habe die "große Datenmengen vor dem Schreiben lesen" nach einem direkten out.write (Puffer) hinzugefügt. Das war mein erster Versuch, der dieses Problem noch hatte. Der Codec gibt 32 Byte Chunks aus. – AAccount

+0

Es ist zu kompliziert, wenn es im Wesentlichen nicht mit dem übereinstimmt, was ich oben geschrieben habe. Je größer der Puffer und je mehr Sie versuchen, ihn zu füllen, bevor Sie etwas schreiben, desto größer wird die Latenz. Sicherlich ist das offensichtlich? – EJP

+0

Vielen Dank für die Überprüfung. Wirklich gehen über den Ruf der Pflicht hinaus :). Ich bin mir nicht sicher, ob Sie meine Bearbeitung gesehen haben, aber ich habe den Code wieder in meine ursprüngliche Implementierung geändert, von der ich denke, dass sie nicht die unnötigen Dinge enthält, die Sie nicht mögen. Die ursprüngliche Implementierung hat immer noch das Problem. – AAccount

Verwandte Themen