Новости о продуктах
Улучшение воспроизведения мультимедиа: предварительная загрузка с помощью Media3 — Часть 1
8 минут чтения

В современных приложениях, ориентированных на мультимедиа, обеспечение плавного и бесперебойного воспроизведения является ключом к приятному пользовательскому опыту. Пользователи ожидают, что их видео начнут воспроизводиться мгновенно и без пауз.
Основная проблема — задержка. Традиционно видеоплеер начинает свою работу — подключение, загрузку, анализ, буферизацию — только после того, как пользователь выбрал контент для воспроизведения. Такой реактивный подход медленен для современных коротких видеороликов. Решение — проактивный подход. Нам нужно предвидеть, что пользователь будет смотреть дальше, и подготовить контент заранее. В этом и заключается суть предварительной загрузки.
Основные преимущества предварительной загрузки включают в себя:
- 🚀 Более быстрый запуск воспроизведения: Видео уже готовы к воспроизведению, что обеспечивает более быстрые переходы между элементами и более мгновенный запуск.
- 📉 Сокращение буферизации: Благодаря предварительной загрузке данных вероятность зависания воспроизведения, например, из-за проблем с сетью, значительно снижается.
- ✨ Более плавная работа для пользователей: сочетание более быстрого запуска и меньшего количества буферизации создает более плавное и бесперебойное взаимодействие, которым пользователи смогут наслаждаться.
В этой серии из трех частей мы познакомим вас с мощными утилитами Media3 для (пред)загрузки компонентов и подробно их рассмотрим.
- В первой части мы рассмотрим основы: понимание различных стратегий предварительной загрузки, доступных в Media3, включение PreloadConfiguration и настройку DefaultPreloadManager, а также возможность предварительной загрузки элементов вашим приложением. К концу этой статьи вы сможете предварительно загружать и воспроизводить медиафайлы с заданным вами рейтингом и продолжительностью.
- Во второй части мы углубимся в более сложные темы DefaultPreloadManager: использование слушателей для аналитики, изучение передовых методов, готовых к внедрению в производство, таких как шаблон скользящего окна и пользовательские общие компоненты DefaultPreloadManager и ExoPlayer.
- В третьей части мы подробно рассмотрим кэширование на диске с помощью 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
Для динамических пользовательских интерфейсов, таких как вертикальные ленты или карусели, где следующий элемент определяется взаимодействием пользователя, подходит API PreloadManager. Это новый мощный автономный компонент в библиотеке Media3 ExoPlayer, специально разработанный для упреждающей предварительной загрузки. Он управляет набором потенциальных источников мультимедиа, расставляя приоритеты в зависимости от близости к текущему положению пользователя, и предлагает детальный контроль над тем, что именно нужно предварительно загрузить, что подходит для сложных сценариев, таких как динамические ленты коротких видеороликов.
Настройка вашего 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 , что гарантирует корректное совместное использование их внутренних компонентов.
Вот и всё! Теперь у вас есть менеджер, готовый принимать инструкции.
Настройка длительности и ранжирования с помощью TargetPreloadStatusControl
Что если вы хотите предварительно загрузить, скажем, 10 секунд видео? Вы можете указать положение ваших медиафайлов в карусели, и DefaultPreloadManager будет определять приоритет загрузки элементов в зависимости от того, насколько близко он находится к тому файлу, который пользователь воспроизводит в данный момент.
Если вы хотите контролировать длительность предварительной загрузки элемента, вы можете указать это в возвращаемом значении параметра DefaultPreloadManager.PreloadStatus.
Например,
- Элемент «А» имеет наивысший приоритет, загрузите 5 секунд видео.
- Пункт «B» имеет средний приоритет, но когда до него дойдете, загрузите 3 секунды видео.
- Пункт «C» имеет меньший приоритет, он предназначен только для загрузки рельсов.
- Пункт «D» еще менее приоритетен, просто подготовьтесь.
- Все остальные предметы находятся далеко. Ничего не загружайте предварительно.
Такой детальный контроль поможет оптимизировать использование ресурсов, что рекомендуется для бесперебойного воспроизведения.
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. Вы можете получить 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 на правой стороне, где наблюдается более быстрая загрузка, в то время как левая сторона демонстрирует существующий интерфейс. Вы также можете посмотреть пример кода для демонстрации. (Бонус: там также отображается задержка запуска для каждого видео)

Что дальше?
На этом первая часть завершена! Теперь у вас есть инструменты для создания динамической системы предварительной загрузки. Вы можете использовать PreloadConfiguration для предварительной загрузки следующего элемента плейлиста в ExoPlayer или настроить DefaultPreloadManager , добавлять и удалять элементы на лету, настраивать целевой статус предварительной загрузки и корректно получать предварительно загруженный контент для воспроизведения.
Во второй части мы углубимся в изучение DefaultPreloadManager . Мы рассмотрим, как отслеживать события предварительной загрузки, обсудим лучшие практики, такие как использование скользящего окна для предотвращения проблем с памятью, и заглянем под капот пользовательских общих компонентов ExoPlayer и DefaultPreloadManager.
У вас есть какие-либо отзывы, которыми вы хотели бы поделиться ? Мы будем рады их услышать.
Следите за обновлениями и сделайте ваше приложение быстрее! 🚀
Продолжить чтение

Новости о продуктах
Добро пожаловать во вторую часть нашей серии из трех статей о предварительной загрузке медиаконтента с помощью Media3. Эта серия призвана помочь вам в процессе создания высокоэффективных приложений для Android с низкой задержкой и быстрым откликом.
Mayuri Khinvasara Khabya • Чтение, 9 минут

Новости о продуктах
Android Studio Panda 4 теперь стабильна и готова к использованию в продакшене. В этом релизе появились режим планирования, прогнозирование следующего изменения и многое другое, что делает создание высококачественных Android-приложений проще, чем когда-либо.
Matt Dyor • 5 мин чтения

Новости о продуктах
Если вы — разработчик Android-приложений, стремящийся внедрить в них инновационные функции искусственного интеллекта, то недавно мы выпустили новые мощные обновления.
Thomas Ezan • 3 мин чтения
Будьте в курсе событий
Получайте еженедельно самые свежие новости о разработке Android прямо на свою электронную почту.



