2011-03-14 15 views
10

Tengo una AudioTrack en mi aplicación, que está configurada en modo Stream. Quiero escribir audio que recibo a través de una conexión inalámbrica. El AudioTrack se declara así:AudioTrack: Reproducción de sonido entrando a través de WiFi

mPlayer = new AudioTrack(STREAM_TYPE, 
         FREQUENCY, 
         CHANNEL_CONFIG_OUT, 
         AUDIO_ENCODING, 
         PLAYER_CAPACITY, 
         PLAY_MODE); 

Cuando los parámetros se definen como:

private static final int FREQUENCY = 8000, 
         CHANNEL_CONFIG_OUT = AudioFormat.CHANNEL_OUT_MONO, 
         AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT, 
         PLAYER_CAPACITY = 2048, 
         STREAM_TYPE = AudioManager.STREAM_MUSIC, 
         PLAY_MODE = AudioTrack.MODE_STREAM; 

Sin embargo, cuando escribo datos a la AudioTrack con write(), se reproducirá el entrecortado ... llaman

byte[] audio = packet.getData(); 
mPlayer.write(audio, 0, audio.length); 

se hace cada vez que se recibe un paquete sobre la conexión de red. ¿Alguien tiene una idea de por qué suena entrecortado? Tal vez tiene algo que ver con la conexión WiFi en sí? No lo creo, ya que el sonido no suena horrible al revés, cuando envío datos desde el teléfono Android a otra fuente a través de UDP. Entonces, el sonido suena completo y sin interrupciones ... ¿Alguien tiene una idea de por qué sucede esto?

+0

¿Has probado a jugar con 'PLAYER_CAPACITY'? Creo que deberías usar 'getMinBufferSize()' para obtener el valor adecuado, como se muestra aquí: http://developer.android.com/reference/android/media/AudioTrack.html – gary

+0

Lo he intentado ... Pero el los datos que obtengo de la red suelen ser inferiores a los de 4096 I (actualmente) que obtengo en este dispositivo de getMinBufferSize. Configurarlo en bufferize ThaMe90

+0

Eché un segundo vistazo a esto, en mi caso, el picado se debe a la gran cantidad de paquetes faltantes/retransmisores. En algún sistema de recursos limitados como Android, el programa debe procesar cada paquete UDP individual lo suficientemente rápido para evitar que falte el paquete. – yorkw

Respuesta

5

Lo he resuelto parcialmente colocando el mPlayer.write(audio, 0, audio.length); en su propio Thread. Esto quita algo de la agilidad (debido a que la escritura es una llamada de bloqueo), pero todavía suena entrecortado después de un buen segundo o 2. Todavía tiene un retraso significativo de 2-3 segundos.

new Thread(){ 
    public void run(){ 
     byte[] audio = packet.getData(); 
     mPlayer.write(audio, 0, audio.length); 
    } 
}.start(); 

Sólo un poco anónimo Thread que aporta la escritura ahora ...

Alguien tiene una idea sobre cómo resolver este problema?


Editar:

Después de un ulterior comprobación y depuración, me he dado cuenta de que este es un problema con obtainBuffer. He visto el java code of the AudioTrack y el C++ code of AudioTrack Y me he dado cuenta de que solo puede aparecer en el código C++.

if (__builtin_expect(result!=NO_ERROR, false)) { 
    LOGW( "obtainBuffer timed out (is the CPU pegged?) " 
      "user=%08x, server=%08x", u, s); 
    mAudioTrack->start(); // FIXME: Wake up audioflinger 
    timeout = 1; 
} 

Me he dado cuenta de que hay un FIXME en este fragmento de código. : < Pero de todos modos, ¿podría alguien explicar cómo funciona este código C++? He tenido alguna experiencia con ella, pero nunca fue tan complicado como este ...


Edición 2:

He intentado algo diferente ahora, con la diferencia de que los datos I buffer Recibo, y luego cuando el buffer se llena con algunos datos, se escribe al jugador. Sin embargo, el jugador sigue consumiendo durante unos pocos ciclos, luego aparece la advertencia obtainBuffer timed out (is the CPU pegged?), y no hay ningún dato escrito para el jugador hasta que vuelva a cobrar vida ... Después de eso, obtendrá continuamente datos escrito hasta que el buffer se vacíe.

Otra pequeña diferencia es que actualizo un archivo al reproductor ahora. Es decir, leerlo en pedazos, escribir esos fragmentos en el búfer. Esto simula los paquetes que se reciben a través de wifi ...

Estoy empezando a preguntarme si esto es solo un problema de sistema operativo que tiene Android, y no es algo que pueda resolver por mi cuenta ... Alguien tiene alguna idea ¿en ese?


Datos 3:

que he hecho más pruebas, pero esto no me cualquier sí ayuda aún más. Esta prueba me muestra que solo recibo un retraso cuando intento escribir en AudioTrack por primera vez. Esto tarda de 1 a 3 segundos en completarse. Hice esto usando el siguiente fragmento de código:

long beforeTime = Utilities.getCurrentTimeMillis(), afterTime = 0; 
mPlayer.write(data, 0, data.length); 
afterTime = Utilities.getCurrentTimeMillis(); 
Log.e("WriteToPlayerThread", "Writing a package took " + (afterTime - beforeTime) + " milliseconds"); 

Sin embargo, me da los siguientes resultados: Logcat Image http://img810.imageshack.us/img810/3453/logcatimage.png

Estos muestran que el retraso se produce inicialmente al principio, después de lo cual el AudioTrack mantiene la obtención de datos continuamente ... Realmente necesito arreglar esto ...

+0

__builtin_pect es una sugerencia de optimización. La lógica se reduce a "si hay un error, luego wite para iniciar sesión, llame a start() en el audiotrack y configure el indicador de tiempo de espera". 'result' se establece en la línea anterior,' result = cblk-> cv.waitRelative (cblk-> lock, seconds (1)); ', que parece ser una espera en una variable de condición. Entonces parece que hay algo mal con el esquema de seguridad de hilos. – AShelly

+0

Otra evidencia de un error de seguridad de subprocesos en este código es esta línea de más adelante en la misma función: 'LOGW_IF (timeout," *** ADVERTENCIA GRAVE *** obtainBuffer() agotó el tiempo de espera pero no necesitó ser bloqueado. Nos recuperamos, pero esto no debería suceder (usuario =% 08x, servidor =% 08x) ", u, s);' – AShelly

+0

Sí, pero nunca recibo esa advertencia ... Solo la "regular" ... – ThaMe90

7

¿Sabe cuántos bytes por segundo está recibiendo, el tiempo promedio entre paquetes se compara y el tiempo máximo entre paquetes? Si no, ¿puedes agregar código para calcularlo?

Necesita promediar 8000 muestras/segundo * 2 bytes/muestra = 16,000 bytes por segundo para mantener la secuencia llena.

Un espacio de más de 2048 bytes/(16000 bytes/segundo) = 128 milisegundos entre los paquetes entrantes hará que la transmisión se seque y el audio tartamudee.

Una forma de evitarlo es aumentar el tamaño del búfer (PLAYER_CAPACITY). Un búfer más grande será más capaz de manejar la variación en el tamaño y la tasa del paquete entrante. El costo de la estabilidad adicional es un retraso mayor en el inicio de la reproducción mientras espera que el buffer se llene inicialmente.

+0

En una estimación aproximada, obtengo unos 67.200 bytes por segundo. Aproximadamente (aproximadamente) 70 paquetes con valor de 960 bytes. Digo más o menos porque esto es a través de una conexión por cable. Cuando paso por la red inalámbrica, supongo que recibiré unos 50 (tal vez un poco más, quizás un poco menos) paquetes por segundo, lo que me da 48,000 bytes por segundo. Yo diría que eso es suficiente para mantenerlo lleno. : o – ThaMe90

+0

Después de algunas depuraciones y de revisar varios logcats, me he dado cuenta de que es un error "getBuffer timed out" ... Todavía trato de solucionarlo ... – ThaMe90

+0

¿Alguna vez resolvió este problema?Por favor publique las actualizaciones. También tengo un problema similar ... ¡¡¡gracias !!! – aProgrammer

Cuestiones relacionadas