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

Чтобы воспроизвести мультимедиа файл, необходимы две составляющие: сам медиаплеер, который проигрывает аудио и видео, а также юзер-интерфейс, отображающий некоторые метаданные (например, название, продолжительность аудиотрека или видео и текущее состояние воспроизведения).

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

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

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

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

Таким образом, подобная архитектура позволяет более полно использовать возможности как самого плеера, так и всей системы в целом.

Libraries

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

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

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

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

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

Множество схожих библиотек, их возможности и недостатки, различные условия совместимости — всё это затрудняет выбор библиотеки для использования.

Для удобства разработчиков в Jetpack Media3 решили объединить эти библиотеки. 

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

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

  3. Был реализован один модуль с компонентами пользовательского интерфейса и вида плеера и ещё один модуль с API для работы с медиасессиями системы. 

Результатом стал androidx.media3, который предоставляет связанный набор библиотек для различных вариантов использования мультимедиа. 

Foreground playback

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

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

Для решения этого вопроса в Media3 отказались от коннектора. Media3 предоставляет ExoPlayer в качестве своего стандартного плеера — и именно в нём реализуется интерфейс Player. 

Поэтому в Google обновили MediaSession и виджеты UI, чтобы они воспринимали тот же Player-интерфейс, связав их напрямую друг с другом. 

Background playback

В случае фонового воспроизведения всё немного сложнее. Архитектура разделена между сервисом, содержащим проигрыватель, и activity для пользовательского интерфейса. 

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

Как уже говорилось ранее, плеер не может взаимодействовать напрямую с сеансом — требуется коннектор. Также он необходим для медиаконтроллера и нашего UI. В результате возникает та же проблема, что и с проигрыванием на переднем плане —֫ соединители усложняют код, что приводит к сбоям.

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

PlayerService

А теперь давайте рассмотрим, как создать приложение с воспроизведением аудио- и видеоконтента в фоне. 

В первую очередь создаём экземпляр ExoPlayer. Затем запускаем медиасеанс и передаем ему нашу реализацию плеера.

Media3 будет автоматически обновлять медиасеанс в зависимости от состояния проигрывателя. Соединительные слои в этом случае не потребуются.

Переходим к Activity, где мы собираемся отобразить пользовательский интерфейс. 

Устанавливаем ссылку на сеанс воспроизведения в методе OnStart() жизненного цикла Activity. 

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

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

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

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

После настройки UI обновляется так же, как и при воспроизведении на переднем плане, когда пользовательский интерфейс и плеер находятся в одной и той же Activity. Это работает даже в том случае, если ваш сервис и плеер запущены в отдельном процессе.

Working with other apps

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

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

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

Во втором случае мы передаём контент воспроизведения — другие приложения могут использовать собственный юзер-интерфейс. Это важно, например, для Android Auto, который предоставляет собственный, удобный для водителя UI для нашего контента.

Комментарии (0)