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 error (1, -1004) también conocido como MEDIA_ERROR_IO intentando transmitir música en Samsung S3
- Reproducción de vídeo en TextureView
- Reproducción de vídeo desde el directorio caché de la aplicación
- Reproducción de FLV / HTTP en la aplicación de Android
- URL del stream de Media Player y, a continuación, reproducir url siguiente
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.
- ¿Cuál es la diferencia entre MediaPlayer y VideoView en Android
- Transmisión desde M3U en Android
- Transmisión de audio a teléfonos móviles, ¿qué tecnología utilizar?
- Tecnología para aplicaciones como teamviewer en android
- ¿Cómo obtener continuamente los datos de marco de la API de Camera2 para el propósito de transmisión?
- ¿Debería un MediaPlayer ejecutarse en hilos separados?
- Android: transmisión de la cámara como mjpeg
- Configuración de encabezados para streaming de vídeo mp4 y reproducción de archivos con Exoplayer
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).
- Desplazamiento del cuadro de selección de tiempo en Samsung S4 OS 4.3
- Cómo almacenar la clave con Android Key Store Proveedor