Audio stream buffering

Eugenious picture Eugenious · Dec 29, 2010 · Viewed 23.2k times · Source

I need to play live audio stream, actually it is radio. The problem is that I also need to manage 20 minute buffer for streaming. As far as I understand it's not easy to implement with android.

Firstly I checked MediaPlayer, but it doesn't provide any methods for buffer management. In fact you even can't set buffer size directly.

Secondly I tried to manage buffer using local files: progressively download the stream to a temporary files and switch them. But when you want to switch to following file (change datasource in MediaPlayer), audio is not played continuously. You can hear short interruptions.

The last idea is to use stream proxy. Usually it is used for playing streams in Android versions below 8. In stream proxy you create ServerSocket, read from audio stream and write to player. So actually I can manage buffering there. I can cache stream and write to MediaPlayer whatever I want. But. It doesn't work with Android 8.

I got exception: Connection reset by peer java.net.SocketException: Connection reset by peer. MediaPlayer 8 doesn't want to read data from socket.

Thus I have two questions: 1) What other way to implement stream buffering? 2) how to adapt StreamProxy for android 8?

Any ideas are appreciated.

Thanks

Answer

Eugenious picture Eugenious · Dec 30, 2010

I use the same StreamProxy that guys used for NPR project - https://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/StreamProxy.java

So it gets original audio stream:

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

And writes from this stream to client socket:

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

(You can get whole code from the link above.)

And finally how they initialize the player (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();

So they check SDK version. But if I omit this check and use proxy for SDK 8 as well I get an exception. It is strange that MediaPlayer even doesn't try to read the stream:

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)

It seems that MediaPlayer became more clever. And If i pass such url "http://127.0.0.1:%d/%s" it wants to get not only bytes but "full" http responce.

Also I wonder if there any other ways implement buffering? As I know the MediaPlayer consumes only files and urls. Solution with files doesn't work. So I have to use socket for stream transmitting.

Thanks