Я использую сокет в качестве прокси для MediaPlayer, поэтому я могу загружать и расшифровывать mp3-аудио перед его записью в сокет. Это похоже на пример, показанный в новостном приложении NPR, однако я использую его для всех версий Android 2.1 — 4 атм.
Код NPR StreamProxy — http://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/StreamProxy.java
Моя проблема в том, что воспроизведение выполняется быстро для 2.1–2.3, но в Android 4.0 ICS MediaPlayer буферизует слишком много данных перед запуском слушателя onPrepared.
Пример объема данных, записанных в Socket OutputStream перед onPrepared():
На SGS2 с 2.3.4 — onPrepared() после ~ 133920 байт
На Nexus S с 4.0.4 — onPrepared() после ~ 961930 байт
Это также происходит на Galaxy Nexus.
Как ни странно, эмулятор 4.0 не буферизует столько данных, сколько устройства 4.0. Кто-нибудь сталкивался с подобной проблемой с MediaPlayer на ICS?
РЕДАКТИРОВАТЬ
Вот как прокси пишет в сокет. В этом примере это из CipherInputStream, загруженного из файла, но то же самое происходит, когда он загружается из 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();
Заголовки HTTP, которые записываются в MediaPlayer до аудио..
private String buildHttpHeader(final int contentLength) {
final StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.1 200 OK\r\n");
sb.append("Content-Length: ").append(contentLength).append("\r\n");
sb.append("Accept-Ranges: bytes\r\n" );
sb.append("Content-Type: audio/mpeg\r\n");
sb.append("Connection: close\r\n" );
sb.append("\r\n");
return sb.toString();
}
Я искал альтернативные реализации, но, поскольку я зашифровал звук, а MediaPlayer не поддерживает InputStreams в качестве источника данных, мой единственный вариант (я думаю...) — использовать такой прокси.
Опять же, это работает достаточно хорошо для Android 2.1–2.3, но в ICS MediaPlayer буферизует огромное количество этих данных перед воспроизведением.
РЕДАКТИРОВАТЬ 2:
Дальнейшее тестирование показало, что эта проблема возникает и на SGS2 после обновления до Android 4.0.3. Таким образом, похоже, что реализация буферизации MediaPlayer значительно изменилась в версии 4.0. Это расстраивает, поскольку API не дает возможности изменить поведение.
РЕДАКТИРОВАТЬ 3:
Создана ошибка Android. Пожалуйста, добавляйте комментарии и звездочки там же http://code.google.com/p/android/issues/detail?id=29870
РЕДАКТИРОВАТЬ 4:
Мой код воспроизведения довольно стандартный. У меня есть вызов start() на MediaPlayer в моем методе onPrepared().
mCurrentPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mCurrentPlayer.setDataSource(url);
mCurrentPlayer.prepareAsync();
Пробовали использовать только prepare(), а также рекомендованный ajacian81 способ, но безрезультатно.
Должен добавить, что недавно сотрудник Google перезвонил мне по моему вопросу и подтвердил, что размер буфера был намеренно увеличен в ICS (для HD-контента). Разработчикам API было предложено добавить возможность установки размера буфера в MediaPlayer.
Хотя я думаю, что этот запрос на изменение API уже существовал до меня, поэтому я бы никому не советовал затаить дыхание.