Почему для Android MediaPlayer требуется много времени для подготовки некоторых живых потоков для воспроизведения?
Я нахожу большие различия во времени, которое требуется для Media Media Player для подготовки к воспроизведению в реальном времени с различными потоками.
Жесткие данные
Я добавил logging между prepareAsync() и обратным вызовом onPrepared (MediaPlayer mp) и протестировал несколько потоков несколько раз каждый. Времена для каждого потока были очень последовательными (+/- одна секунда), и вот результаты:
- поток новостей MPR: 27 секунд (http://newsstream1.publicradio.org:80/)
- MPR классический музыкальный поток: 15 секунд (http://classicalstream1.publicradio.org:80/)
- MPR Текущий поток: 7 секунд (http://currentstream1.publicradio.org:80/)
- поток PRI: 52 секунды (http://pri-ice.streamguys.biz/pri1)
Тесты были выполнены на Nexus S с Android 2.3.4 на 3G-соединении (~ 1100 Кбит/с).
Воспроизведение безпоточных аудиофайлов MP3 не является проблемой.
Вот фрагменты того, как я играю в потоки:
Подготовить MediaPlayer:
...
mediaPlayer.setDataSource(playUrl);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.prepareAsync();
...
Затем в onPrepared (MediaPlayer mp):
mediaPlayer.start();
Почему так долго нужно готовить некоторые потоки, но не другие? Вышеприведенные данные, по-видимому, предполагают, что это может быть основано на количестве данных, которые были буферизованы, а не на продолжительности буферизованного аудиоконтента. Неужели это действительно так?
Обновление: Я тестировал прямую трансляцию на физических устройствах с Android 1.6, 2.2 и 2.3.4 и эмуляторами с 1.6, 2.1, 2.2, 2.3.1 и 2.3.3. Я вижу только длительную задержку на 2.3.3 и 2.3.4. Старшие версии запускают воспроизведение в течение 5 секунд.
Ответы
Ответ 1
Кажется, что это буферизация фиксированного количества данных, а не фиксированное количество времени. Для тех, кто не знает битрейты различных типов потоков NPR с верхней части головы, данные выглядят так:
Помимо расхождения между двумя потоками 128 Кбит/с, существует очень хорошая корреляция между продолжительностью битрейта и буферизации.
В любом случае Android является открытым исходным кодом, поэтому вы всегда можете посмотреть, что он делает. К сожалению, prepareAsync()
и prepare()
являются собственными методами, и кажется, что события, связанные с буфером, отправляются также из собственного процесса.
Вы пытались подключить OnBufferingUpdateListener
к MediaPlayer для получения более тонких обновлений состояния буфера? Возможно, было бы интересно сравнить скорость, с которой доставляются события, и в каком проценте буфер заполняется каждым событием в разных потоках. Вы можете перекрестно ссылаться на битрусы потока, и если 4 секунды буферизации со скоростью 32 кбит/с заполняет буфер таким же процентом, как и 1 секунда буферизации со скоростью 128 кбит/с, тогда, я думаю, вы найдете свой ответ.
Ответ 2
Переключить MediaPlayer
на FFmpegMediaPlayer работает намного лучше, чем MediaPlayer
, если вы хотите протестировать свои потоки, вы можете сделать это через demo, что у них есть.
Ответ 3
Недавно я отладил эту же проблему с провайдером потокового аудио. Эта проблема связана со сценарием и потоковыми источниками 32 кбит/с и ниже. Мы прошли через те же потоки, измеряя время отклика на 24, 32, 48, 64 и 128 кбит/с.
- 24 → 46 секунд для начала потоковой передачи
- 32 → 24 секунды для начала потоковой передачи.
- 48 → 2 секунды для начала потоковой передачи
- 64 → 2 секунды для начала потоковой передачи
- 128 → 2 секунды для начала потоковой передачи
Это из последовательного беспроводного соединения, усредненного по 10 попыток с каждой скоростью передачи. Ключ, как заметил Тревис, заключался в том, что stagefright не мог понять, как долго буферизовать звук. Иногда я вижу сообщение "error: 1, -21492389" или около того, что, казалось, бесшумно разбивало игрока stagefright. Я попытался отследить это и в конце концов пришел к выводу, что очень медленные потоки (суб 24 кбит/с), по-видимому, вызывают переполнение буфера, потому что они будут буферизоваться до тех пор, пока на устройстве не будет пространства для аудиопотока.
Я хотел добавить, что OnBufferingUpdateListener
вообще не срабатывал для меня во время всего теста. Я не знаю, для чего это. Я думаю, что единственный способ узнать, как происходит загрузка, - это прокси-загрузка загрузки, аналогично упомянутому выше приложению NPR.
Ответ 4
Если вы загружаете из Icecast, посмотрите на параметр burst-size
:
Размер пакета - это объем данных (в байтах), которые нужно разбить клиенту при подключении. Подобно пакетному подключению, это необходимо для быстрого заполнения предварительный буфер, используемый медиаплеерами. Значение по умолчанию - 64 килобайта, что является типичный размер, используемый большинством клиентов, поэтому его изменение обычно обязательный. Этот параметр применяется ко всем точкам монтирования, если они не переопределены в настройки монтирования. Убедитесь, что это значение меньше размера очереди, если необходимо, увеличьте размер очереди, чтобы быть больше желаемого выбросоопасного размер. Несоблюдение этого требования может привести к прерыванию клиента-слушателя попыток подключения из-за первоначального пакета, приводящего к соединению уже превысив предел размера очереди.
Я увеличил burst-size
до 131072 на моем сервере, и теперь мое приложение для Android на основе MediaPlayer
воспроизводит потоки без особых задержек.
Ответ 5
Я пробовал это с 10 точками данных, тремя быстрыми, 7 медленными.
Это последовательный, то есть быстрый поток быстрый и медленный всегда медленный.
Я думаю, что это связано с тем, что сервер поставил "длину контента", Android не знает, сколько нужно буферировать, если длина содержимого не задана должным образом.
Может быть неправильно, не дошел до проводов.
Ответ 6
Когда у меня возникла эта проблема, я решил проверить, доступен ли поток, прежде чем открывать плеер. Если вы заставите пользователя долго ждать, и музыка начнет его нормально (это не так, но пусть говорят, что все в порядке).
Худший сценарий - заставить его долго ждать, и музыка никогда не начнется!
Итак, у нас есть две ситуации:
- Сценарий в прямом эфире, как и радиостанция.
- Записанный mp3 файл, который доступен онлайн.
В сценарии радио мы можем проверить, принимает ли порт соединения (состояние открытия/закрытия). Если он открыт, подготовьте плеер к музыке, иначе не подготовьте его вообще.
public static boolean isLiveStreamingAvailable() {
SocketAddress sockaddr = new InetSocketAddress(STREAMING_HOST, STREAMING_PORT);
// Create your socket
Socket socket = new Socket();
boolean online = true;
// Connect with 10 s timeout
try {
socket.connect(sockaddr, 10000);
} catch (SocketTimeoutException stex) {
// treating timeout errors separately from other io exceptions
// may make sense
return false;
} catch (IOException iOException) {
return false;
} finally {
// As the close() operation can also throw an IOException
// it must caught here
try {
socket.close();
} catch (IOException ex) {
// feel free to do something moderately useful here, eg log the event
}
}
return true;
}
В сценарии mp3 файл все немного отличается. Вы должны проверить код ответа, который следует после HTTP-запроса.
public static boolean isRecordedStreamingAvailable() {
try {
HttpURLConnection.setFollowRedirects(false);
// note : you may also need
// HttpURLConnection.setInstanceFollowRedirects(false)
HttpURLConnection con =
(HttpURLConnection) new URL(RECORDED_URL).openConnection();
con.setRequestMethod("HEAD");
return (con.getResponseCode() == HttpURLConnection.HTTP_OK);
}
catch (Exception e) {
e.printStackTrace();
return false;
}
}