Prácticas recomendadas para la transmisión de audio

Estoy escribiendo una aplicación para reproducir audio desde un servidor remoto. He intentado varias maneras de implementar la transmisión de audio, pero todos ellos no son lo suficientemente buenos para mí. Eso es lo que he intentado:

Uso ingenuo de MediaPlayer

Algo como:

MediaPlayer player = new MediaPlayer(); player.setDataSource(context, Uri.parse("http://whatever.com/track.mp3")); player.prepare(); player.start(); 

(O prepareAsync , no importa)

Pero MediaPlayer estándar es bastante inestable al reproducir contenido remoto. A menudo cae o detiene la reproducción y no puedo procesar esto. Por otro lado, quiero implementar el almacenamiento en caché de medios. Pero no he encontrado ninguna forma de obtener contenido almacenado en búfer de MediaPlayer para guardarlo en algún lugar en el dispositivo.

Implementación del almacenamiento en búfer personalizado

Luego se convirtió en una idea para descargar archivos de medios por trozos, combinarlos en un archivo local y reproducir este archivo. Descargar todo el archivo puede ser lento debido a la mala conexión, por lo que estará bien para descargar lo suficiente inicialmente pieza, a continuación, iniciar la reproducción y continuar la descarga y anexar archivo local. Además, tenemos la funcionalidad de almacenamiento en caché.

Suena como un plan, pero no siempre funciona. Funciona perfectamente en HTC Sensation XE, pero no se detuvo en la reproducción de tabletas 4.1 después de terminar esta pieza inicial. No sé, por qué es así. He hecho preguntas sobre esto, pero no recibí respuestas.

Uso de dos MediaPlayers

He creado dos instancias de MediaPlayer y he intentado hacerlas cambiar entre sí. La lógica es la siguiente:

  • Iniciar la descarga de la pieza inicial de los medios
  • Cuando se descarga, inicie la reproducción a través de currentMediaPlayer. El resto de medios sigue descargando
  • Cuando la pieza descargada se haya reproducido (1 seg antes de finalizar), prepare secondMediaPlayer con el mismo archivo de origen (como se añadió durante la reproducción)
  • 261 ms antes del final de currentMediaPlayer – pausa, comienzo secundario, ajuste secundario como actual, programar la preparación del jugador secundario siguiente.

La fuente:

 private static final String FILE_NAME="local.mp3"; private static final String URL = ...; private static final long FILE_SIZE = 7084032; private static final long PREPARE_NEXT_PLAYER_OFFSET = 1000; private static final int START_NEXT_OFFSET = 261; private static final int INIT_PERCENTAGE = 3; private MediaPlayer mPlayer; private MediaPlayer mSecondaryPlayer; private Handler mHandler = new Handler(); public void startDownload() { mDownloader = new Mp3Downloader(FILE_NAME, URL, getExternalCacheDir()); mDownloader.setDownloadListener(mInitDownloadListener); mDownloader.startDownload(); } private Mp3Downloader.DownloadListener mInitDownloadListener = new Mp3Downloader.DownloadListener() { public void onDownloaded(long bytes) { int percentage = Math.round(bytes * 100f / FILE_SIZE); // Start playback when appropriate piece of media downloaded if (percentage >= INIT_PERCENTAGE) { mPlayer = new MediaPlayer(); try { mPlayer.setDataSource(mDownloader.getDownloadingFile().getAbsolutePath()); mPlayer.prepare(); mPlayer.start(); mHandler.postDelayed(prepareSecondaryPlayerRunnable, mPlayer.getDuration() - PREPARE_NEXT_PLAYER_OFFSET); mHandler.postDelayed(startNextPlayerRunnable, mPlayer.getDuration() - START_NEXT_OFFSET); } catch (IOException e) { Log.e(e); } mDownloader.setDownloadListener(null); } } }; // Starting to prepare secondary MediaPlayer private Runnable prepareSecondaryPlayerRunnable = new Runnable() { public void run() { mSecondaryPlayer = new MediaPlayer(); try { mSecondaryPlayer.setDataSource(mDownloader.getDownloadingFile().getAbsolutePath()); mSecondaryPlayer.prepare(); mSecondaryPlayer.seekTo(mPlayer.getDuration() - START_NEXT_OFFSET); } catch (IOException e) { Log.e(e); } } }; // Starting secondary MediaPlayer playback, scheduling creating next MediaPlayer private Runnable startNextPlayerRunnable = new Runnable() { public void run() { mSecondaryPlayer.start(); mHandler.postDelayed(prepareSecondaryPlayerRunnable, mSecondaryPlayer.getDuration() - mPlayer.getCurrentPosition() - PREPARE_NEXT_PLAYER_OFFSET); mHandler.postDelayed(startNextPlayerRunnable, mSecondaryPlayer.getDuration() - mPlayer.getCurrentPosition() - START_NEXT_OFFSET); mPlayer.pause(); mPlayer.release(); mPlayer = mSecondaryPlayer; } }; 

Una vez más – suena, como un plan, pero no funciona perfectamente. Los momentos de cambio de MediaPlayers son bastante audibles. Aquí tengo la situación opuesta: en la tableta 4.1 está bien, pero en HTC Sensation hay retardos evidentes.

También intenté implementar diferentes técnicas de descarga. He implementado la descarga en fragmentos de 10Kb y en marcos de MP3. No sé exactamente, pero parece que en el caso de MP3 frames seekTo y empezar a trabajar mejor. Pero es sólo un sentimiento, no sé la explicación.

StreamingMediaPlayer

Vi esta palabra varias veces durante la búsqueda de Google y encontré esta implementación: https://code.google.com/p/mynpr/source/browse/trunk/mynpr/src/com/webeclubbin/mynpr/StreamingMediaPlayer.java?r = 18

Es una solución que todos usan?

Si es así, es triste, porque no está funcionando bien para mí también. Y no veo ninguna idea nueva en la implementación.

Entonces, la pregunta

¿Cómo implementan el streaming de audio en sus aplicaciones? No creo que soy la única persona que se enfrentó a problemas como este. Debe haber algunas buenas prácticas.

En mi caso uso FFMPEG con OpenSL ES. La desventaja es la complejidad. Usted debe estar familiarizado con un montón de cosas: JNI, OpenSL, FFMPEG. También es difícil depurar (comparando con la aplicación Java android pura). En su caso le sugiero que pruebe Media API de bajo nivel. Lo único es la falta de ejemplos. Pero hay una prueba de la unidad que demuestra cómo usted puede manejar audio (usted necesita cambiar InputStream referencia – línea 82).

  • Corregir archivo 3GP después de la transmisión desde Android Media Recorder
  • ANDROID: ¿Hay reproductor multimedia gratuito de terceros para streaming de vídeo MPEG4?
  • Cómo obtener el tipo de contenido de ShoutCast 7.html
  • Dispositivo Android como receptor para el perfil A2DP
  • Comportamiento extraño al transmitir contenido con Android MediaPlayer en HTC Desire con Android 2.2
  • SurfaceView, SurfaceTexture y MediaPlayer no pueden reproducir mi video en android
  • Transmisión en vivo a android utilizando 4.1 HTML 5
  • Streaming con Android MediaPlayer: captura de errores y almacenamiento en memoria intermedia
  • Problema de MediaRecorder en Android Lollipop
  • MMS de vídeo de flujo de Android y m3u8
  • Reproducir stream de RTMP de audio en android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.