Android - размер буфера MediaPlayer в ICS 4.0

Я использую сокет в качестве прокси для 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 байт

Это также происходит на Галактике Нексус.

Как ни странно, эмулятор 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 был до того, как я пришел, поэтому я бы никому не советовал задерживать дыхание.

2 ответа

Можно ли будет увидеть код, с которого вы запускаете () MediaPlayer?

Вы используете STREAM_MUSIC тип аудиопотока?

player.setAudioStreamType(AudioManager.STREAM_MUSIC);

Вы также экспериментировали между player.prepareAsync(); и player.prepare();?

В прошлом году, как я помню, была похожая проблема, решение которой заключалось в следующем: start, pause и затем onPrepared to start():

player.setAudioStreamType(AudioManager.STREAM_MUSIC); 
player.setDataSource(src); 
player.prepare(); 
player.start(); 
player.pause(); 
player.setOnPreparedListener(new OnPreparedListener() {     
@Override
                public void onPrepared(MediaPlayer mp) {
                    player.start();                
                }
          });

Маловероятно, чтобы это было исправлением в этом случае, но пока вы вращаете свои колеса, это, возможно, стоит того.

Для меня решение было использовать MediaCodec с AudioTrack, я нашел все, что мне нужно знать здесь:

Это может быть решением: http://www.piterwilson.com/blog/2014/03/15/mediacodec-mediaextractor-and-audiotrack-to-the-rescue/

Другие вопросы по тегам