21 сентября 2022

Новые возможности AndroidX Media и ExoPlayer

Создание приложений с функциями воспроизведения мультимедиа связана с рядом трудностей, которые усложняют разработку. Однако в этом году появилась возможность использовать Jetpack Media3 — решение, которое полностью меняет процесс взаимодействия с мультимедиа. Об этой библиотеке и ее возможностях расскажет Android-разработчик CleverPumpkin Сергей Смирнов.
Jetpack Media3 - иллюстрация
Чтобы воспроизвести мультимедиа файл, необходимы две составляющие: сам медиаплеер, который проигрывает аудио и видео, а также юзер-интерфейс, отображающий некоторые метаданные (например, название, продолжительность аудиотрека или видео и текущее состояние воспроизведения).

Кроме того, пользовательский интерфейс позволяет взаимодействовать с плеером через кнопки — приложение получает команды «Воспроизведение», «Пауза», «Навигация по времени» и выводит уведомления при изменении состояния.

Проблема состоит в том, что в этой архитектуре система Android и другие приложения не получают информации о воспроизведении мультимедиа. Как следствие, пользователи лишаются удобных способов просмотра и управления плеером вне нашего приложения.
Jetpack Media3 - иллюстрация
Для того чтобы решить эту проблему, необходимо подключить плеер к сеансу мультимедиа — тогда система получит данные о том, что сейчас воспроизводится файл, и предоставит возможность управления. При этом заходить в само приложение не потребуется.

Благодаря этому становятся доступны многие полезные интеграции. Например, можно управлять воспроизведением файла через умные часы или воспользоваться кнопками на наушниках. В дальнейшем планируется реализация управления из других приложений, а также посредством голосового помощника.

Таким образом, подобная архитектура позволяет более полно использовать возможности как самого плеера, так и всей системы в целом.
Jetpack Media3 - иллюстрация
Libraries
Главный вопрос, с которыми сталкиваются разработчики в этой сфере — это выбор подходящей библиотеки для реализации функции воспроизведения и управления мультимедиа.

Решение этого вопроса — Jetpack Media3. Но чтобы лучше понять её преимущества, для начала сделаем обзор некоторых библиотек, которые использовались ранее.

Androidx.media2. В ней реализованы модули, отвечающие за воспроизведение, компоненты интерфейса, контроль сеанса мультимедиа, а также общие структуры данных и функции.

ExoPlayer
. Это библиотека для операций воспроизведения мультимедиа. Её используют в сотнях тысяч приложений, включая разработанный нами Kassir.ru — здесь с его помощью проигрываются сторис и видео в баннерах.

В этих API есть модули, которые практически дублируют друг друга и выполняют в целом одинаковые функции.

Также существуют и другие библиотеки, включая оригинальную compat-библиотеку androidx.media.

Множество схожих библиотек, их возможности и недостатки, различные условия совместимости — всё это затрудняет выбор библиотеки для использования.
Jetpack Media3 - иллюстрация
Для удобства разработчиков в Jetpack Media3 решили объединить эти библиотеки.

  1. Был создан один общий модуль.

  2. ExoPlayer был стандартизирован, при этом сохранился его обширный набор вспомогательных модулей.

  3. Был реализован один модуль с компонентами пользовательского интерфейса и вида плеера и ещё один модуль с API для работы с медиасессиями системы.
Результатом стал androidx.media3, который предоставляет связанный набор библиотек для различных вариантов использования мультимедиа.
Jetpack Media3 - иллюстрация
Foreground playback
Давайте опишем главные юзкейсы и выделим преимущества Jetpack Media3 по сравнению с прошлыми версиями.

Рассмотрим вариант воспроизведения контента, при котором приложение расположено на переднем плане. Здесь можно использовать архитектуру, когда UI, плеер и медиасеанс находятся в одной activity. В этом случае медиасеанс позволяет поддерживать события мультимедийных клавиш и элементы управления отображением «картинка в картинке».

Раньше в этом подходе возникали сложности, так как с предыдущими API сеанс мультимедиа не мог напрямую взаимодействовать с плеером. Требовался объект-коннектор, который бы переводил команды и обратные вызовы между этими компонентами. Чтобы реализовать весь набор команд, которые может получать сеанс мультимедиа, и для обработки различных состояний плеера требовалось писать много кода. Из-за этого появлялись дополнительные сложности, и повышался риск возникновения ошибок.
Jetpack Media3 - иллюстрация
Для решения этого вопроса в Media3 отказались от коннектора. Media3 предоставляет ExoPlayer в качестве своего стандартного плеера — и именно в нём реализуется интерфейс Player.

Поэтому в Google обновили MediaSession и виджеты UI, чтобы они воспринимали тот же Player-интерфейс, связав их напрямую друг с другом.
Jetpack Media3 - иллюстрация
Background playback
В случае фонового воспроизведения всё немного сложнее. Архитектура разделена между сервисом, содержащим проигрыватель, и activity для пользовательского интерфейса.

Сервис запускает сеанс мультимедиа, который используется для объявления воспроизведения и передачи команд проигрывателю. И внутри activity мы создаем медиаконтроллер, который служит для связи с сеансом мультимедиа.

Как уже говорилось ранее, плеер не может взаимодействовать напрямую с сеансом — требуется коннектор. Также он необходим для медиаконтроллера и нашего UI. В результате возникает та же проблема, что и с проигрыванием на переднем плане —֫ соединители усложняют код, что приводит к сбоям.
Jetpack Media3 - иллюстрация
Чтобы решить этот вопрос, был реализован общий интерфейс Player. Теперь ExoPlayer напрямую совместим с сеансом мультимедиа, а медиаконтроллер напрямую совместим с UI. В результате таких изменений получается код, который легче поддерживать и который меньше подвержен ошибкам.
Jetpack Media3 - иллюстрация
PlayerService
А теперь давайте рассмотрим, как создать приложение с воспроизведением аудио- и видеоконтента в фоне.

В первую очередь создаём экземпляр ExoPlayer. Затем запускаем медиасеанс и передаем ему нашу реализацию плеера.
Media3 будет автоматически обновлять медиасеанс в зависимости от состояния проигрывателя. Соединительные слои в этом случае не потребуются .
Jetpack Media3 - иллюстрация
Переходим к Activity, где мы собираемся отобразить пользовательский интерфейс.
Устанавливаем ссылку на сеанс воспроизведения в методе OnStart() жизненного цикла Activity.

1. Формируем токен для сеанса, к которому мы хотим подключиться.

2. Создаем MediaController, который асинхронно подключается к медиасеансу.

3. Также можно подключить Listener на установку соединения.

4. Поскольку MediaController — это всего лишь реализация интерфейса Player, мы можем передать его непосредственно в playerView.

После настройки UI обновляется так же, как и при воспроизведении на переднем плане, когда пользовательский интерфейс и плеер находятся в одной и той же Activity. Это работает даже в том случае, если ваш сервис и плеер запущены в отдельном процессе.
Jetpack Media3 - иллюстрация
Working with other apps
В завершение коснёмся вопроса, как наше приложение может работать с другими приложениями в системе Android.

Существуют два основных варианта.

В первом случае мы открываем доступ к медиасеансу, чтобы другие приложения и устройства могли управлять нашим медиаплеером и воспроизведением. Для пользователя такой вариант может быть наиболее удобен.

Во втором случае мы передаём контент воспроизведения — другие приложения могут использовать собственный юзер-интерфейс. Это важно, например, для Android Auto, который предоставляет собственный, удобный для водителя UI для нашего контента.
Jetpack Media3 - иллюстрация
Подведем итог. Jetpack media3 — это новый «дом» для всех библиотек поддержки мультимедиа. Эта библиотека вместе с ExoPlayer предоставляет все базовые возможности воспроизведения, а также позволяет использовать упрощенную архитектуру для большинства кейсов и разумное поведение приложения.
Есть идея?