Улучшение воспроизведения медиаконтента: предзагрузка с помощью Media3 – часть 1
Время на чтение: 8 минут
В современных медиаприложениях плавное и непрерывное воспроизведение – залог удобства для пользователей. Пользователи ожидают, что видео будет запускаться мгновенно и воспроизводиться без пауз.
Основная сложность – задержка. Обычно видеопроигрыватель начинает работу (подключается, скачивает, анализирует, буферизует) только после того, как пользователь выбрал контент для воспроизведения. Такой подход не подходит для современных коротких видео. Решение – действовать на опережение. Нам нужно предугадать, что пользователь будет смотреть дальше, и подготовить контент заранее. В этом и заключается суть предзагрузки.
Основные преимущества предварительной загрузки:
- 🚀 Более быстрый запуск воспроизведения. Видео уже готовы к показу, поэтому переходы между ними происходят быстрее, а воспроизведение начинается раньше.
- 📉 Меньше буферизации. Благодаря упреждающей загрузке данных воспроизведение реже прерывается, например из-за проблем с сетью.
- ✨ Улучшение пользовательского опыта. Более быстрый запуск и меньшее количество буферизаций делают взаимодействие с приложением более плавным и приятным для пользователей.
В этой серии из трех статей мы расскажем о мощных утилитах Media3 для (предварительной) загрузки компонентов.
- В первой части мы рассмотрим основы: разберем различные стратегии предзагрузки, доступные в Media3, включим PreloadConfiguration и настроим DefaultPreloadManager, чтобы приложение могло предзагружать элементы. Прочитав эту запись, вы сможете предварительно загружать и воспроизводить мультимедийные объекты с настроенными рейтингом и продолжительностью.
- Во второй части мы рассмотрим более сложные темы, связанные с DefaultPreloadManager: использование прослушивателей для аналитики, а также рекомендации по подготовке к выпуску, например шаблон скользящего окна и пользовательские общие компоненты DefaultPreloadManager и ExoPlayer.
- В части 3 мы подробно рассмотрим кеш диска с помощью DefaultPreloadManager.
Предзагрузка спешит на помощь! 🦸♀️
Основная идея предзагрузки проста: загружать медиаконтент до того, как он понадобится. К тому времени, когда пользователь переходит к следующему видео, первые сегменты видео уже загружены и доступны, готовы к немедленному воспроизведению.
Представьте, что вы пришли в ресторан. На загруженной кухне не ждут заказа, чтобы начать резать лук. 🧅 Они готовятся заранее. Предзагрузка – это подготовка видеопроигрывателя к работе.
Если включить предварительную загрузку, это поможет минимизировать задержку при присоединении, когда пользователь переходит к следующему элементу до того, как буфер воспроизведения достигнет следующего элемента. Подготавливается первый период следующего окна, а также буферизуются образцы видео, аудио и текста. Затем предварительно загруженный период ставится в очередь в проигрывателе с буферизованными образцами, которые сразу же доступны и готовы к передаче в кодек для рендеринга.
В Media3 есть два основных API для предзагрузки, каждый из которых подходит для разных вариантов использования. Выбор подходящего API – это первый шаг.
1. Предзагрузка объектов плейлиста с помощью PreloadConfiguration
Это простой подход, который подходит для линейных последовательных медиа, таких как плейлисты, где порядок воспроизведения предсказуем (например, серии сериала). Вы передаете проигрывателю полный список мультимедийных объектов с помощью API плейлиста ExoPlayer и задаете для проигрывателя PreloadConfiguration. После этого он автоматически предварительно загружает следующие объекты в последовательности в соответствии с конфигурацией. Этот API пытается оптимизировать задержку при присоединении, когда пользователь переходит к следующему объекту до того, как буфер воспроизведения уже перекрывается со следующим объектом.
Предварительная загрузка начинается только тогда, когда для текущего воспроизведения не загружаются медиаданные. Это позволяет избежать конкуренции за пропускную способность с основным воспроизведением.
Если вы все ещё не уверены, нужна ли вам предзагрузка, этот API – отличный способ попробовать ее.
player.preloadConfiguration =
PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L)При указанной выше конфигурации PreloadConfiguration проигрыватель пытается предварительно загрузить пять секунд медиаконтента для следующего объекта в плейлисте.
Если вы включили предварительную загрузку плейлистов, ее можно отключить с помощью метода PreloadConfiguration.DEFAULT:
player.preloadConfiguration = PreloadConfiguration.DEFAULT
2. Предзагрузка динамических списков с помощью PreloadManager
Для динамических интерфейсов, таких как вертикальные ленты или карусели, где следующий элемент определяется взаимодействием с пользователем, подходит PreloadManager API. Это новый мощный автономный компонент в библиотеке Media3 ExoPlayer, специально разработанный для упреждающей предварительной загрузки. Он управляет набором потенциальных источников MediaSource, определяя их приоритет на основе близости к текущей позиции пользователя, и предлагает детальный контроль над тем, что нужно предварительно загрузить. Это подходит для сложных сценариев, таких как динамические фиды коротких видео.
Как настроить PreloadManager
DefaultPreloadManager – это каноническая реализация PreloadManager.
Конструктор DefaultPreloadManager может создавать как DefaultPreloadManager, так и любые экземпляры ExoPlayer, которые будут воспроизводить предварительно загруженный контент. Чтобы создать DefaultPreloadManager, вам нужно передать TargetPreloadStatusControl, который менеджер предзагрузки может запрашивать, чтобы узнать, сколько нужно загрузить для элемента. Ниже мы приведем пример TargetPreloadStatusControl и объясним, как он работает.
val preloadManagerBuilder = DefaultPreloadManager.Builder(context, targetPreloadStatusControl) val preloadManager = val preloadManagerBuilder.build() // Build ExoPlayer with DefaultPreloadManager.Builder val player = preloadManagerBuilder.buildExoPlayer()
Для ExoPlayer и DefaultPreloadManager необходимо использовать один и тот же builder, чтобы компоненты правильно распределялись между ними.
Вот и всё! Теперь у вас есть менеджер, готовый получать инструкции.
Как настроить продолжительность и ранжирование с помощью TargetPreloadStatusControl
Что если вы хотите предварительно загрузить, скажем, 10 секунд видео? Вы можете указать положение мультимедийных объектов в карусели, и DefaultPreloadManager будет загружать их в зависимости от того, насколько близко они находятся к объекту, который пользователь воспроизводит в данный момент.
Если вы хотите контролировать, какую часть объекта нужно предварительно загрузить, вы можете указать это с помощью возвращаемого значения DefaultPreloadManager.PreloadStatus.
Например,
- Элемент A имеет наивысший приоритет, загружается 5 секунд видео.
- Элемент B имеет средний приоритет, но когда вы дойдете до него, загрузите 3 секунды видео.
- Объект C имеет более низкий приоритет, загружаются только треки.
- Пункт «Г» имеет ещё меньший приоритет, просто подготовьтесь.
- Все остальные элементы находятся далеко, поэтому ничего не нужно загружать заранее.
Такой детальный контроль поможет вам оптимизировать использование ресурсов, что рекомендуется для бесперебойного воспроизведения.
import androidx.media3.exoplayer.DefaultPreloadManager.PreloadStatus
class MyTargetPreloadStatusControl(
currentPlayingIndex: Int = C.INDEX_UNSET
) : TargetPreloadStatusControl<Int,PreloadStatus> {
// The app is responsible for updating this based on UI state
override fun getTargetPreloadStatus(index: Int): PreloadStatus? {
val distance = index - currentPlayingIndex
// Adjacent items (Next): preload 5 seconds
if (distance == 1) {
// Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED and suggest loading // 5000ms from the default start position
return PreloadStatus.specifiedRangeLoaded(5000L)
}
// Adjacent items (Previous): preload 3 seconds
else if (distance == -1) {
// Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED //and suggest loading 3000ms from the default start position
return PreloadStatus.specifiedRangeLoaded(3000L)
}
// Items two positions away: just select tracks
else if (distance) == 2) {
// Return a PreloadStatus that is labelled by STAGE_TRACKS_SELECTED
return PreloadStatus.TRACKS_SELECTED
}
// Items four positions away: just select prepare
else if (abs(distance) <= 4) {
// Return a PreloadStatus that is labelled by STAGE_SOURCE_PREPARED
return PreloadStatus.SOURCE_PREPARED
}
// All other items are too far away
return null
}
}Примечание. PreloadManager может сохранять предварительно загруженными как предыдущие, так и следующие элементы, тогда как PreloadConfiguration будет смотреть только на следующие элементы.
Управление объектами предзагрузки
Теперь, когда менеджер создан, вы можете начать давать ему задания. Когда пользователь прокручивает ленту, вы определяете, какие видео будут показаны дальше, и добавляете их в менеджер. Взаимодействие с PreloadManager – это управляемый состояниями обмен данными между вашим интерфейсом и механизмом предзагрузки.
1. Добавить мультимедийные объекты
При заполнении фида необходимо сообщить менеджеру, какие медиафайлы нужно отслеживать. Если вы только начинаете, то можете добавить весь список, который хотите предварительно загрузить. Затем вы сможете добавлять в список отдельные объекты по мере необходимости. Вы полностью контролируете, какие элементы находятся в списке предварительной загрузки, а значит, вы также должны управлять тем, что добавляется и удаляется из менеджера.
val initialMediaItems = pullMediaItemsFromService(/* count= */ 20)
for (index in 0 until initialMediaItems.size) {
preloadManager.add(
initialMediaItems.get(index),index)
)
}Менеджер начнет получать данные для этого MediaItem в фоновом режиме.
После добавления сообщите менеджеру, что нужно повторно оценить новый список (указав, что что-то изменилось, например был добавлен или удален элемент или пользователь переключился на воспроизведение нового элемента).
preloadManager.invalidate()
2. Получение и воспроизведение объекта
Теперь перейдем к основной логике воспроизведения. Когда пользователь решит посмотреть видео, вам не нужно будет создавать новый объект MediaSource. Вместо этого вы запрашиваете у PreloadManager уже подготовленный ответ. Вы можете получить MediaSource из Preload Manager с помощью MediaItem.
Если объект, полученный из PreloadManager, имеет значение null, это означает, что mediaItem ещё не предварительно загружен или не добавлен в PreloadMamager, поэтому вы выбираете установить mediaItem напрямую.
// When a media item is about to display on the screen
val mediaSource = preloadManager.getMediaSource(mediaItem)
if (mediaSource!= null) {
player.setMediaSource(mediaSource)
} else {
// If mediaSource is null, that mediaItem hasn't been added yet.
// So, send it directly to the player.
player.setMediaItem(mediaItem)
}
player.prepare()
// When the media item is displaying at the center of the screen
player.play()Подготовив MediaSource, полученный из PreloadManager, вы сможете легко перейти от предзагрузки к воспроизведению, используя данные, которые уже находятся в памяти. Это позволяет ускорить запуск.
3. Синхронизируйте текущий индекс с интерфейсом
Поскольку фид или список может быть динамическим, важно уведомлять PreloadManager о текущем индексе воспроизведения, чтобы он всегда мог отдавать приоритет элементам, ближайшим к текущему индексу, для предзагрузки.
preloadManager.setCurrentPlayingIndex(currentIndex) // Need to call invalidate() to update the priorities preloadManager.invalidate()
4. Удаление объекта
Чтобы менеджер работал эффективно, удаляйте объекты, которые больше не нужно отслеживать, например те, которые находятся далеко от текущего местоположения пользователя.
// When an item is too far from the current playing index preloadManager.remove(mediaItem)
Чтобы удалить все элементы сразу, вызовите preloadManager.reset().
5. Освободите менеджера
Когда PreloadManager больше не нужен (например, при удалении интерфейса), его необходимо освободить, чтобы освободить ресурсы. Рекомендуем сделать это там, где вы уже освобождаете ресурсы проигрывателя. Рекомендуется освобождать менеджер раньше проигрывателя, поскольку проигрыватель может продолжать воспроизведение, если вам больше не нужна предварительная загрузка.
// In your Activity's onDestroy() or Composable's onDispose preloadManager.release()
Время для деморежима
Посмотрите, как это работает 👍
В демонстрации ниже показано , как PreloadManager влияет на скорость загрузки. Справа – с PreloadManager, слева – без него. Вы также можете посмотреть пример кода для демоверсии. (Бонус: также отображается задержка запуска для каждого видео)
Что дальше?
Вот и все, что мы хотели рассказать в первой части. Теперь у вас есть инструменты для создания системы динамической предзагрузки. Вы можете использовать PreloadConfiguration, чтобы предварительно загрузить следующий элемент плейлиста в ExoPlayer, или настроить DefaultPreloadManager, добавлять и удалять элементы на лету, настроить целевой статус предварительной загрузки и правильно получить предварительно загруженный контент для воспроизведения.
Во второй части мы подробнее рассмотрим DefaultPreloadManager. Мы расскажем, как отслеживать события предзагрузки, обсудим рекомендации, например использование скользящего окна, чтобы избежать проблем с памятью, и заглянем под капот пользовательских общих компонентов ExoPlayer и DefaultPreloadManager.
Хотите поделиться отзывом? Мы будем рады услышать ваше мнение.
Следите за новостями и ускоряйте работу своего приложения! 🚀
-
Новости о продуктахПредставляем вторую часть серии из трех статей о предварительной загрузке медиаконтента с помощью Media3. В этой серии видео мы расскажем, как создавать приложения для Android, которые быстро реагируют на действия пользователя и воспроизводят медиаконтент с низкой задержкой.
Mayuri Khinvasara Khabya • Время на чтение: 9 минут -
Новости о продуктахСегодня на Саммите для разработчиков Android мы объявляем о широкой доступности многих инструментов и API, которые помогут вам преодолевать повседневные трудности и дадут больше гибкости при создании отличных приложений для Android.
Matthew McCullough • Время на чтение: 4 минуты -
Новости о продуктахAndroid Studio Rabbit 2 теперь стабильна и готова к использованию в рабочей среде. В этом выпуске представлены ключевые функции, которые помогут вам в разработке, например Bring Your Own Agent, новые функции XR Emulator, обновление LeakCanary, новый инструмент управления навыками агента, код-ревью с помощью ИИ и многое другое.
Matthew Warner • Время на чтение: 3 минуты
Получайте еженедельную рассылку с последними новостями о разработке для Android.