2010-12-29 24 views
21

Necesito reproducir transmisión de audio en vivo, en realidad es radio. El problema es que también necesito administrar un búfer de 20 minutos para la transmisión. Por lo que yo entiendo, no es fácil de implementar con Android.Buffer de transmisión de audio

Primero revisé MediaPlayer, pero no proporciona ningún método para la gestión de búfer. De hecho, incluso no puede establecer el tamaño del búfer directamente.

En segundo lugar, traté de administrar el búfer con los archivos locales: descargar progresivamente la secuencia a un archivo temporal y cambiarlos. Pero cuando quiere cambiar al siguiente archivo (cambiar el origen de datos en MediaPlayer), el audio no se reproduce continuamente. Puedes escuchar interrupciones cortas.

La última idea es usar el proxy de transmisión. Por lo general, se usa para reproducir secuencias en versiones de Android inferiores a 8. En el proxy de flujo, crea ServerSocket, lee de la transmisión de audio y escribe en el reproductor. Así que en realidad puedo administrar el almacenamiento en búfer allí. Puedo almacenar en caché y escribir en MediaPlayer lo que quiera. Pero. No funciona con Android 8.

Obtuve la excepción: Connection reset by peer java.net.SocketException: Connection reset by peer. MediaPlayer 8 no quiere leer datos del socket.

Por lo tanto, tengo dos preguntas: 1) ¿Qué otra manera de implementar almacenamiento en búfer de secuencia? 2) cómo adaptar StreamProxy para android 8?

Se aprecian todas las ideas.

Gracias

+0

"2) cómo adaptar StreamProxy para android 8?" ... A menos que mostrar el código que está utilizando, es imposible que alguien pueda ayudar. – Squonk

+1

¿Cómo está escribiendo stream en MediaPlayer? ¿Puede explicar un poco más? –

Respuesta

8

utilizo el mismo StreamProxy que los tipos utilizados para el proyecto NPR - https://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/StreamProxy.java

por lo que recibe la corriente de audio original:

String url = request.getRequestLine().getUri(); 
    HttpResponse realResponse = download(url); 
    ... 
    InputStream data = realResponse.getEntity().getContent(); 

Y escribe desde esta corriente de socket del cliente:

byte[] buff = new byte[1024 * 50]; 
    while (isRunning && (readBytes = data.read(buff, 0, buff.length)) != -1) { 
    client.getOutputStream().write(buff, 0, readBytes); 
    } 

(Puede obtener el código completo en el enlace a Bove)

Y finalmente cómo inicializar el jugador (PlaybackService):.

if (stream && sdkVersion < 8) { 
    if (proxy == null) { 
     proxy = new StreamProxy(); 
     proxy.init(); 
     proxy.start(); 
    } 
    String proxyUrl = String.format("http://127.0.0.1:%d/%s", proxy.getPort(), url); 
    playUrl = proxyUrl; 
    } 
    ... 
    mediaPlayer.setDataSource(playUrl); 
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 
    mediaPlayer.prepareAsync(); 

Así que comprobar la versión del SDK. Pero si omito esta verificación y uso el proxy para SDK 8 también recibo una excepción. Es extraño que MediaPlayer incluso no intenta leer la secuencia:

12-30 15:09:41.576: DEBUG/StreamProxy(266): downloading... 
12-30 15:09:41.597: DEBUG/StreamProxy(266): reading headers 
12-30 15:09:41.597: DEBUG/StreamProxy(266): headers done 
12-30 15:09:41.647: DEBUG/StreamProxy(266): writing to client 
12-30 15:09:41.857: INFO/AwesomePlayer(34): mConnectingDataSource->connect() returned - 1007 
12-30 15:09:41.857: ERROR/MediaPlayer(266): error (1, -1007) 
12-30 15:09:41.867: ERROR/MediaPlayer(266): Error (1,-1007) 
12-30 15:09:41.867: WARN/AudioService(266): onError(1, -1007) 
12-30 15:09:41.867: WARN/AudioService(266): MediaPlayer refused to play current item. Bailing on prepare. 
12-30 15:09:41.867: WARN/AudioService(266): onComplete() 
12-30 15:09:42.097: ERROR/StreamProxy(266): Connection reset by peer 
     java.net.SocketException: Connection reset by peer 
     at org.apache.harmony.luni.platform.OSNetworkSystem.writeSocketImpl(Native Method) 
     at org.apache.harmony.luni.platform.OSNetworkSystem.write(OSNetworkSystem.java:723) 
     at org.apache.harmony.luni.net.PlainSocketImpl.write(PlainSocketImpl.java:578) 
     at org.apache.harmony.luni.net.SocketOutputStream.write(SocketOutputStream.java:59) 
     at com.skyblue.service.media.StreamProxy.processRequest(StreamProxy.java:204) 
     at com.skyblue.service.media.StreamProxy.run(StreamProxy.java:103) 
     at java.lang.Thread.run(Thread.java:1096) 

Parece que MediaPlayer se volvió más inteligente. Y si paso tal url "http://127.0.0.1:%d/%s", quiere obtener no solo bytes, sino respuesta http "completa".

También me pregunto si hay otras formas de implementar el almacenamiento en búfer? Como sé, MediaPlayer solo consume archivos y URL. La solución con archivos no funciona. Así que tengo que usar un socket para transmitir en stream.

Gracias

+2

Hola Eugenio, El enlace que publicaste del proyecto NPR ya no se cierra ¿puedes publicar un enlace válido? –

+0

Aquí está el enlace correcto: https://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/StreamProxy.java – stanri

+0

Hola Eugenio, encontraste la solución al problema original? ¿Te ayudó la solución publicada por Brack? – burakk

2

el MediaPlayer de SDK 8 no serían capaces de leer una dirección URL de proxy. Pero según mi trabajo actual, esto difiere de un dispositivo a otro. En mi Samsung ACE (SDK 8), la conexión proxy funciona bien, pero en mi HTC Incredible S, la conexión proxy daría el mismo problema que usted.Una conexión directa a la transmisión de audio funciona bien, pero esto causa fallas y boops en algunos dispositivos como EVO en Sprint.

¿Obtuvo una resolución para este problema? ¿Como manejaste esto?

-Hari

+0

He experimentado el mismo comportamiento, y estoy 99% seguro de que está vinculado al reproductor multimedia subyacente. En la mayoría de los dispositivos SDK 8 que usan el reproductor multimedia actual (AwesomePlayer/StageFright), se producirá el "reinicio de la conexión". Sin embargo, algunos dispositivos usan reproductores multimedia obsoletos (PVPlayer) y no se producirá ningún error. – brack

5

Acabo de probar la revisión siguiente, y funciona. Este problema está causado por el "\ n" utilizado en los encabezados en el método processRequest() de StreamProxy. Cambiar esto a "\ r \ n" debería hacer desaparecer el error.

En cuanto a la implementación de la transmisión personalizada, he usado esto en el pasado, aunque parece ser principalmente para la transmisión de radio de forma infinita. http://blog.pocketjourney.com/2009/12/27/android-streaming-mediaplayer-tutorial-updated-to-v1-5-cupcake/

+0

La URL de publicación de blog no existe. – Murat

3

Cuando busca u omite o la conexión se pierde y MediaPlayer se vuelve a conectar al servidor proxy, debe enviar esta respuesta con el estado 206 después de recibir la solicitud y rango (int) del cliente.

String headers += "HTTP/1.1 206 Partial Content\r\n"; 
headers += "Content-Type: audio/mpeg\r\n"; 
headers += "Accept-Ranges: bytes\r\n"; 
headers += "Content-Length: " + (fileSize-range) + "\r\n"; 
headers += "Content-Range: bytes "+range + "-" + fileSize + "/*\r\n"; 
headers += "\r\n"; 

Y cuando reciba una petición de MediaPlayer que no contiene Rango en la cabecera HTTP, a continuación, se solicita un nuevo archivo de flujo, en este caso el encabezado de respuesta debe tener este aspecto:

String headers = "HTTP/1.1 200 OK\r\n"; 
headers += "Content-Type: audio/mpeg\r\n"; 
headers += "Accept-Ranges: bytes\r\n"; 
headers += "Content-Length: " + fileSize + "\r\n"; 
headers += "\r\n"; 

¡Disfrútalo!

1

Me doy cuenta de que esta pregunta tiene 5 años, pero en caso de que alguien más esté deambulando: ExoPlayer es una biblioteca de Google que le da mucho más control sobre opciones como el tamaño del búfer.

//DefaultUriDataSource – For playing media that can be either local or loaded over the network. 
    DefaultUriDataSource dataSource = new DefaultUriDataSource(WgtechApplication.getAppContext(), 
      Util.getUserAgent(WgtechApplication.getAppContext(), WgtechApplication.class.getSimpleName())); 
    Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE); 

    //ExtractorSampleSource – For formats such as MP3, M4A, MP4, WebM, MPEG-TS and AAC. 
    ExtractorSampleSource sampleSource = new ExtractorSampleSource(Uri.parse(RADIO_STREAMING_URL), 
      dataSource, allocator, BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE); 
    player.prepare(new MediaCodecAudioTrackRenderer(sampleSource)); 

Aquí hay un pequeño proyecto que creé para aprender a usarlo. https://github.com/feresr/MyMediaPlayer

http://developer.android.com/guide/topics/media/exoplayer.html https://github.com/google/ExoPlayer

Cuestiones relacionadas