Проблема с сетевым носителем JellyBean для Android
У меня есть приложение, которое воспроизводит файлы MP3, доступные по общедоступному URL-адресу. К сожалению, сервер не поддерживает потоковое вещание, но Android делает пользователя совершенно приемлемым.
Все работает отлично для всех платформ, за исключением JellyBean. При запросе MP3, JB запрашивает диапазон заголовка в 10 раз. Только после 10-й попытки, похоже, возвращается к старому поведению. Похоже, это уже сообщалось о проблеме.
Я нашел еще один поток SO, где рекомендуется использовать Tranfer-Encoding: chunked header. Но чуть ниже есть комментарий, что это не сработает.
На данный момент у меня нет контроля над тем, чтобы доставлять заголовки ответов, но пока я не смогу этого сделать, я решил найти альтернативу на стороне клиента. (даже в этом случае я могу только вернуть Content-Range, содержащий индексы от 0 до Content-Length - 1. Ex. Content-Range: bytes 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, как вы ее решили?
- Если кто-то из вас попробовал мой подход, что я делаю неправильно в вышеуказанном коде? Если это решение, которое вы использовали, я могу опубликовать остальную часть кода.
Спасибо!
Ответы
Ответ 1
Похоже, вы пытаетесь разработать свой собственный потоковый тип. Это может привести к блокированному или прерванному воспроизведению, потому что вы должны пытаться использовать непрерывный поток информации без вывода байтов для чтения.
В принципе, вам придется учитывать все ситуации, о которых заботится обычный потоковый клиент. Например, иногда некоторые блоки могут быть сброшены или потеряны при передаче; иногда воспроизведение звука может догнать загрузку; CPU начинает отставать, что влияет на воспроизведение; и т.д. и т.д.
Что-то для исследования, если вы хотите продолжить этот путь, было бы реализацией Sliding Window, это, по сути, абстрактный метод, чтобы попытаться поддерживать сетевое подключение всегда активным и текучим. Вы можете найти несколько примеров через google, вот начало: http://en.wikipedia.org/wiki/Sliding_window_protocol
Изменить: одно решение, которое может помочь вам до тех пор, пока это не будет исправлено, будет включать исходный код для MediaPlayer.java
и AudioManager.java
из SDK < 16 в ваш проект и посмотреть, устраняет ли это проблему. Если у вас нет исходного кода, вы можете загрузить его с помощью диспетчера SDK.
Ответ 2
AudioTrack по своей природе блокирует документы (Will block until all data has been written to the audio mixer.
). Я не уверен, что вы читаете из файла и записываете в AudioTrack в той же теме; если да, тогда я предлагаю вам развернуть поток для AudioTrack.