Почему MediaPlayer для Android так долго готовит живые трансляции для воспроизведения? - PullRequest
47 голосов
/ 05 июля 2011

Я нахожу большие различия во времени, которое требуется Android MediaPlayer для подготовки к воспроизведению в прямом эфире с различными потоками.

Жесткие данные

Я добавил ведение журналамежду prepareAsync () и обратным вызовом onPrepared (MediaPlayer mp) и тестировал несколько потоков по несколько раз каждый.Время для каждого потока было очень постоянным (+/- одна секунда), и вот результаты:

  1. Поток новостей MPR: 27 секунд (http://newsstream1.publicradio.org:80/)
  2. Поток классической музыки MPR: 15секунд (http://classicalstream1.publicradio.org:80/)
  3. MPR Текущий поток: 7 секунд (http://currentstream1.publicradio.org:80/)
  4. Поток PRI: 52 секунды (http://pri -ice.streamguys.biz / pri1)

Theтесты проводились на 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 секунд.

Ответы [ 6 ]

24 голосов
/ 05 июля 2011

Похоже, что он буферизует фиксированное количество данных, а не фиксированное количество времени. Для тех, кто не знает скорости передачи данных различных типов потоков NPR, они выглядят так:

  1. Поток новостей MPR: 27 секунд (http://newsstream1.publicradio.org:80/), 64 кбит / с
  2. MPR поток классической музыки: 15 секунд (http://classicalstream1.publicradio.org:80/), 128 кбит / с
  3. MPR Текущий поток: 7 секунд (http://currentstream1.publicradio.org:80/), 128 кбит / с
  4. Поток PRI: 52 секунды (http://pri -ice.streamguys.biz / pri1 ), 32 кбит / с

Помимо несоответствия между двумя потоками 128 кбит / с, существует очень хорошая корреляция между битрейтом и длительностью буферизации.

В любом случае, Android с открытым исходным кодом, поэтому вы всегда можете посмотреть, что он делает . К сожалению, prepareAsync() и prepare() являются нативными методами, и кажется, что связанные с буфером события также отправляются из нативного процесса.

Вы пытались подключить OnBufferingUpdateListener к MediaPlayer, чтобы получать более подробные обновления о состоянии буфера? Может быть интересно сравнить скорость доставки событий и процент заполнения буфера для каждого события в разных потоках. Вы можете сопоставить это с битрейтом потока, и если 4 секунды буферизации при 32 кбит / с заполняют буфер так же, как 1 секунда буферизации при 128 кбит / с, то я думаю, что вы найдете свой ответ.

7 голосов
/ 28 мая 2015

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

4 голосов
/ 12 октября 2012

Я недавно отладил эту же проблему с поставщиком потокового аудио.Проблема связана со сценарием и потоковыми источниками 32 кбит / с и ниже.Мы пошли по той же потоковой передаче, измерив время отклика при 24, 32, 48, 64 и 128 кбит / с.

  • 24 -> 46 секунд, чтобы начать потоковую передачу
  • 32 -> 24 секундычтобы начать потоковую передачу
  • 48 -> 2 секунды, чтобы начать потоковую передачу
  • 64 -> 2 секунды, чтобы начать потоковую передачу
  • 128 -> 2 секунды, чтобы начать потоковую передачу

Это от согласованного беспроводного соединения, усредненного по 10 попыткам на каждую битовую скорость.Ключ, как заметил Трэвис, заключался в том, что сценический страх не мог понять, как долго буферизировать звук.Иногда я видел сообщение «ошибка: 1, -21492389» или около того, что, казалось, бесшумно давало сбой игроку со сцены.Я попытался отследить это и в итоге пришел к выводу, что очень медленные потоки (до 24 кбит / с), по-видимому, вызывают переполнение буфера, поскольку они будут буферизироваться до тех пор, пока на устройстве не закончится место для аудиопотока.

Я хотел добавить, что OnBufferingUpdateListener вообще не срабатывал для меня во время всего этого теста.Я не знаю, для чего это.Я думаю, что единственный способ узнать, как происходит загрузка, - это прокси загрузки, аналогично приложению NPR, упомянутому выше.

2 голосов
/ 30 сентября 2015

Если вы используете потоковую передачу из Icecast, взгляните на настройку burst-size:

Размер пакета - это объем данных (в байтах), который необходимо передать клиенту во время подключения,Подобно пакетному соединению, это позволяет быстро заполнить предварительный буфер, используемый медиа-плеерами.Значение по умолчанию составляет 64 КБ, что является типичным размером, используемым большинством клиентов, поэтому его изменение обычно не требуется.Этот параметр применяется ко всем точкам монтирования, если он не переопределен в настройках монтирования.Убедитесь, что это значение меньше размера очереди, при необходимости увеличьте размер очереди, чтобы он был больше требуемого размера пакета.Невыполнение этого требования может привести к прерванным попыткам подключения к клиентскому прослушивателю из-за первоначального пакета, в результате которого подключение уже превысило ограничение размера очереди.

Я увеличил значение burst-size до 131072 на моем сервере,и теперь мое приложение для Android на базе MediaPlayer воспроизводит потоки без большой задержки.

1 голос
/ 07 декабря 2011

Я пробовал это с 10 точками данных, тремя быстрыми, 7 медленными.Это согласуется с тем, что быстрый поток быстрый, а медленный - всегда медленный.

Я думаю, что это связано с доставкой на сервер «длина контента», Android не знает, сколько буферизировать, если контентдлина не указана должным образом.

Может быть не так, не дошло до проволоки.

0 голосов
/ 04 февраля 2017

Когда у меня возникла эта проблема, я решил проверить, доступен ли поток, прежде чем открывать проигрыватель.Если вы заставите пользователя долго ждать, и музыка начнет нормально (это не так, но, скажем так, все в порядке).Худший сценарий - заставить его долго ждать, и музыка никогда не запустится!Итак, у нас есть 2 ситуации:

  • Сценарий прямой трансляции, например, радиостанция.
  • Записанный mp3-файл, который доступен онлайн.

В сценарии radio мы могли бы проверить, принимает ли порт соединения (состояние открытия / закрытия).Если он открыт, подготовьте проигрыватель к музыке, иначе не готовьте его вообще.

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;
        }
    }
...