Android JellyBean проблема с сетевым медиа
У меня есть приложение, которое воспроизводит файлы MP3, доступные по общедоступному URL. К сожалению, сервер не поддерживает потоковую передачу, но Android делает работу с пользователем вполне приемлемой.
Все работает нормально для всех платформ, кроме JellyBean. При запросе MP3 JB запрашивает Range-Header 10 раз. Только после 10-й попытки он, похоже, возвращается к старому поведению. Похоже, это уже сообщенная проблема.
Я нашел другой поток SO, где рекомендовано решение использовать Tranfer-Encoding: chunked header. Но чуть ниже есть комментарий, что это не работает.
На данный момент я не имею никакого контроля над предоставлением вышеуказанных заголовков ответа, но до тех пор, пока я не смогу это сделать, я подумал о поиске альтернативы на стороне клиента. (даже в этом случае я могу вернуть только Content-Range, который содержит индексы от 0 до Content-Length - 1. Например. Content-Range: байты 0-3123456/3123457).
Я попытался реализовать псевдопоток на стороне клиента:
- Откройте входной поток в MP3.
- Декодируйте входящие байты, используя JLayer. Я нашел расшифровку по этой ссылке.
- Отправьте декодированные байты массива в уже воспроизводимую stream_mode AudioTrack.
Кусок кода, который выполняет декодирование, можно найти там, я только изменил его, чтобы он получал InputStream:
public byte[] decode(InputStream inputStream, int startMs, int maxMs) throws IOException {
ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024);
float totalMs = 0;
boolean seeking = true;
try {
Bitstream bitstream = new Bitstream(inputStream);
Decoder decoder = new Decoder();
boolean done = false;
while (!done) {
Header frameHeader = bitstream.readFrame();
if (frameHeader == null) {
done = true;
} else {
totalMs += frameHeader.ms_per_frame();
if (totalMs >= startMs) {
seeking = false;
}
if (!seeking) {
// logger.debug("Handling header: " + frameHeader.layer_string());
SampleBuffer output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream);
if (output.getSampleFrequency() != 44100 || output.getChannelCount() != 2) {
throw new IllegalArgumentException("mono or non-44100 MP3 not supported");
}
short[] pcm = output.getBuffer();
for (short s : pcm) {
outStream.write(s & 0xff);
outStream.write((s >> 8) & 0xff);
}
}
if (totalMs >= (startMs + maxMs)) {
done = true;
}
}
bitstream.closeFrame();
}
return outStream.toByteArray();
} catch (BitstreamException e) {
throw new IOException("Bitstream error: " + e);
} catch (DecoderException e) {
throw new IOException("Decoder error: " + e);
}
}
Я запрашиваю декодированные байты во временных блоках: начиная с (0, 5000), поэтому сначала у меня будет больший массив для воспроизведения, затем я запрашиваю следующие байтовые массивы, которые охватывают секунду: (5000, 1000), (6000, 1000), (7000, 1000) и т. Д.
Декодирование достаточно быстрое и выполняется в другом потоке, и как только массив декодированных байтов становится доступным, я использую очередь блокировки, чтобы записать ее в AudioTrack, который воспроизводится в другом потоке.
Проблема состоит в том, что воспроизведение не является плавным, поскольку фрагменты не являются непрерывными в дорожке (каждый фрагмент является непрерывным, но добавление в AudioTrack приводит к неаккуратному воспроизведению).
Упаковать:
- Если вы столкнулись с этой проблемой JellyBean, как вы ее решили?
- Если кто-то из вас попробовал мой подход, что я делаю не так в приведенном выше коде? Если это решение, которое вы использовали, я могу опубликовать остальную часть кода.
Спасибо!
2 ответа
Похоже, вы пытаетесь разработать свой собственный тип потоковой передачи. Это может привести к блокированию или прерыванию воспроизведения, поскольку вы должны пытаться непрерывно передавать информацию без исчерпания байтов для чтения.
По сути, вам придется учитывать все ситуации, о которых позаботится обычный потоковый клиент. Например, иногда некоторые блоки могут быть отброшены или потеряны при передаче; иногда воспроизведение аудио может догнать загрузку; процессор начинает отставать, что влияет на воспроизведение; и т. д.
Если вы хотите продолжить идти по этому пути, то вам стоит обратить внимание на реализацию скользящего окна. По сути, это абстрактная техника, позволяющая поддерживать постоянную и плавную сетевую связь. Вы должны быть в состоянии найти несколько примеров через Google, вот с чего начать: http://en.wikipedia.org/wiki/Sliding_window_protocol
Изменить: один из обходных путей, который может помочь вам, пока это не будет исправлено, будет включать в себя исходный код для MediaPlayer.java
а также AudioManager.java
из SDK <16 в ваш проект и посмотрите, решит ли это проблему. Если у вас нет исходного кода, вы можете загрузить его с помощью SDK Manager.
AudioTrack от природы блокирует документы (Will block until all data has been written to the audio mixer.
). Я не уверен, что вы читаете из файла и пишете в AudioTrack в той же теме; если так, то я бы посоветовал вам раскрутить тему для AudioTrack.