I'm using a socket as a proxy to the MediaPlayer so I can download and decrypt mp3 audio before writing it to the socket. This is similar to the example shown in the NPR news app however I'm using this for all Android version 2.1 - 4 atm.
NPR StreamProxy code - http://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/StreamProxy.java
My issue is that playback is fast for 2.1 - 2.3, but in Android 4.0 ICS the MediaPlayer buffers too much data before firing the onPrepared listener.
An example amount of data written to the Socket OutputStream before onPrepared():
On SGS2 with 2.3.4 - onPrepared() after ~ 133920 bytes
On Nexus S with 4.0.4 - onPrepared() after ~ 961930 bytes
This also occurs on the Galaxy Nexus.
Weirdly the 4.0 emulator doesn't buffer as much data as 4.0 devices. Anyone experience a similar issue with the MediaPlayer on ICS?
EDIT
Here's how the proxy is writing to the socket. In this example it's from a CipherInputStream loaded from a file, but the same occurs when it's loaded from the HttpResponse.
final Socket client = (setup above)
// encrypted file input stream
final CipherInputStream inputStream = getInputStream(file);
// setup the socket output stream
final OutputStream output = client.getOutputStream();
// Writing the header
final String httpHeader = buildHttpHeader(file.length());
final byte[] buffer = httpHeader.getBytes("UTF-8");
output.write(buffer, 0, buffer.length);
int writtenBytes = 0;
int readBytes;
final byte[] buff = new byte[1024 * 12]; // 12 KB
while (mIsRunning && (readBytes = inputStream.read(buff)) != -1) {
output.write(buff, 0, readBytes);
writtenBytes += readBytes;
}
output.flush();
output.close();
The HTTP Headers that are written to the MediaPlayer before the audio..
private String buildHttpHeader(final int contentLength) {
final StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.1 200 OK
");
sb.append("Content-Length: ").append(contentLength).append("
");
sb.append("Accept-Ranges: bytes
" );
sb.append("Content-Type: audio/mpeg
");
sb.append("Connection: close
" );
sb.append("
");
return sb.toString();
}
I've looked around for alternate implementations but as I have encrypted audio and the MediaPlayer does not support InputStreams as a data source my only option (I think..) is to use a proxy such as this.
Again, this is working fairly well Android 2.1 - 2.3 but in ICS the MediaPlayer is buffering a huge amount of this data before playing.
EDIT 2 :
Further testing is showing that this is also an issue on the SGS2 once upgraded to Android 4.0.3. So it seems like the MediaPlayer's buffering implementation has changed significantly in 4.0. This is frustrating as the API provides no way to alter the behaviour.
EDIT 3 :
Android bug created. Please add comments and star there as well
http://code.google.com/p/android/issues/detail?id=29870
EDIT 4 :
My playback code is fairly standard.. I have the start() call on the MediaPlayer in my onPrepared() method.
mCurrentPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mCurrentPlayer.setDataSource(url);
mCurrentPlayer.prepareAsync();
Have tried it using just prepare() and also ajacian81's recommended way but to no avail.
I should add that recently a Google employee got back to me about my question and confirmed that the buffer size was intentionally increased in ICS (for HD content). It has been requested to the API developers to add the ability to set a buffer size on MediaPlayer.
Though I think this API change request had been around before I came along so I wouldn't advise anyone to hold their breath.
See Question&Answers more detail:
os