Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
244 views
in Technique[技术] by (71.8m points)

android - Audio stream buffering

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...