Подтвердить что ты не робот

Android media player - как отключить запрос диапазона? (сломанная потоковая передача звука на Nexus 7)

У меня есть приложение для потоковой передачи звука, в котором работает локальный прокси-сервер. Локальный прокси-сервер делает HTTP-соединение с источником потоковой передачи в Интернете, получает и буферизирует локальные потоковые данные. Затем внутри приложения я использую MediaPlayer для подключения к локальному прокси-серверу, используя метод

mediaPlayer.setDataSource(...); // the url of the local proxy server

Все было в порядке (с большим количеством устройств Android и разных версий ОС - 1,5... 4,0), до выпуска Nexus 7.

В Nexus 7 медиаплеер отказывается воспроизводить исходный код с локального прокси-сервера.

Когда я просмотрел журналы, похоже, что MediaPlayer использует внутренние запросы. Мой локальный прокси-сервер не справляется с этим. Он возвращает HTTP/1.0 200 OK и данные. Однако медиаплеер не любит этого и выдает исключение:

Caused by: libcore.io.ErrnoException
?:??: W/?(?): [ 07-18 00:08:35.333  4962: 5149 E/radiobee ]
?:??: W/?(?): : sendto failed: ECONNRESET (Connection reset by peer)
?:??: W/?(?):   at libcore.io.Posix.sendtoBytes(Native Method)
?:??: W/?(?):   at libcore.io.Posix.sendto(Posix.java:146)
?:??: W/?(?):   at libcore.io.BlockGuardOs.sendto(BlockGuardOs.java:
?:??: W/?(?):   at libcore.io.IoBridge.sendto(IoBridge.java:473)
We requested a content range, but server didn't support that. (responded with 200)

В соответствии с параметрами http, если сервер отвечает HTTP/1.0 вместо 1.1, клиент не должен запускать запрос диапазона (1.0 не поддерживает это в любом случае),

также, если сервер не поддерживает запрос диапазона, это должно быть хорошо, если он отвечает 200 OK (и это то, что я делаю), но реализация MediaPlayer на Nexus 7 не нравится.

Я взглянул на эту тему: HTTP: как мне реагировать на? Range: bytes = " когда Range не поддерживается?

где они утверждают, что ответ с 200 OK должен быть достаточно хорошим, но, к сожалению, это не помогает.

Я не уверен, что это проблема с Jelly Bean, или проблема с реализацией Nexus 7, но это все еще проблема для меня, которую я должен решить.

Опять же, нет никаких запросов диапазона на многих других устройствах Android, используя одно и то же приложение. По какой-то причине эти запросы диапазона теперь выполняются на Nexus 7. (Это может случиться и на других устройствах Android, но до сих пор так и не дошло).

Любой возможный способ отключить запросы диапазона для MediaPlayer?

Если их нет, может ли кто-нибудь предложить быстрое исправление для моей логики прокси-сервера (что именно он должен вернуть, если он получит этот запрос диапазона?), если не поменять мою другую логику?

Похоже, что мне нужно вернуть что-то вроде "HTTP/1.0 206 OK\r\nPartial Content\r\n\r\n", но, вероятно, должно быть какое-то значение в конце частичного содержимого - не уверен что должно быть этим.

Ваша помощь будет оценена.

Спасибо..

4b9b3361

Ответ 1

Мы наконец решили это чистым способом. В нашем случае у нас есть полный контроль над потоковым сервером, но я думаю, вы могли бы сделать это и с локальным прокси. Поскольку Build.VERSION_CODES.ICE_CREAM_SANDWICH можно установить заголовки для объекта MediaPlayer. Поскольку наше приложение позволяет пользователю искать внутри аудиопотока, мы реализовали этот заголовок Range на клиенте. Если сервер отвечает соответствующими заголовками, MediaPlayer не будет запрашивать многопрофильное время для запроса потока.

То, что наши заголовки серверов выглядят сейчас для клиента android:

Content-Type: audio/mpeg
Accept-Ranges: bytes
Content-Length: XXXX
Content-Range: bytes XXX-XXX/XXX
Status: 206

Важной частью является код состояния 206 (частичный контент). Если вы не отправляете этот заголовок, клиент android попытается повторно запросить источник, независимо от того, что.

Если ваш проигрыватель не разрешает поиск в потоке, вы всегда можете установить заголовок Range на 0-some arbitrary large number.

Ответ 2

Я работаю над широкомасштабным приложением для потоковой передачи звука (которое использует прокси-сервер локального хоста для потоковой передачи звука в MediaPlayer), и я столкнулся с этой проблемой, как только я получил устройство JellyBean в моих руках в Google I/O 2012.

При тестировании качества нашего приложения на разных устройствах (и с информацией, полученной из наших автоматических журналов сбоев и журналов, отправленных пользователями), мы заметили, что некоторые реализации MediaPlayer вели себя в том, что я назвал бы беспорядочным (а иногда и просто психотическим) способом.

Не вдаваясь в подробности, это то, что я видел: некоторые реализации будут делать несколько запросов (несколько раз 5+) для одного и того же URL-адреса. Эти запросы были немного отличны друг от друга, поскольку каждый из них был для другого байтового диапазона (обычно для первого или последнего 128 байтов). Вывод состоял в том, что MediaPlayer пытался найти встроенные метаданные, после чего через некоторое время он сдался и просто сделал бы обычный запрос без диапазона.

Это не то, что делает реализация JellyBean MediaPlayer, это всего лишь пример безумной и общей фрагментации медиакарты на Android. Однако решение вышеуказанной ситуации также было решением проблемы JellyBean, и это:

Попросите вашего локального прокси ответить закодированное кодирование

Это заменяет заголовок Content-Length заголовком Tranfer-Encoding: chunked. Это означает, что запрашивающий клиент не будет знать общую длину ресурса и, следовательно, не сможет выполнить запрос диапазона, он просто должен иметь дело с кусками по мере их получения.

Как я уже сказал, это взлом, но он работает. Это не без побочных эффектов: прогресс буфера медиаплеера будет неправильным, поскольку он не знает длину звука (является одним из них), чтобы обойти это, вам придется использовать собственные вычисления буферизации (вы потоки откуда-нибудь через ваш прокси-сервер в MediaPlayer, правильно? Значит, вы будете знать общую длину).

Ответ 3

Когда вы ищете или пропускаете или соединение потеряно, и MediaPlayer продолжает переподключение к прокси-серверу, вы должны отправить этот ответ с Статус 206 после того, как вы получите запрос и диапазон (int) от клиента,

String headers += "HTTP/1.0 206 OK\r\n";
headers += "Content-Type: audio/mpeg\r\n";
headers += "Accept-Ranges: bytes\r\n";
headers += "Content-Length: " + (fileSize-range) + "\r\n";
headers += "Content-Range: bytes "+range + "-" + fileSize + "/*\r\n";
headers += "\r\n";