Jetpack Media3 определяет интерфейс Player, который описывает базовые функции для воспроизведения видео- и аудиофайлов. ExoPlayer – это реализация по умолчанию
этого интерфейса в Media3. Мы рекомендуем использовать ExoPlayer, поскольку он предоставляет полный набор функций, охватывающих большинство сценариев воспроизведения, и может быть настроен для обработки любых дополнительных сценариев, которые могут у вас возникнуть. ExoPlayer также абстрагирует фрагментацию устройств и ОС, поэтому ваш код будет стабильно работать во всей экосистеме Android. ExoPlayer включает:
- Поддержка плейлистов.
- Поддержка различных форматов прогрессивной и адаптивной потоковой передачи.
- Поддержка вставки объявлений как на стороне клиента, так и на стороне сервера.
- Поддержка воспроизведения контента, защищенного DRM.
На этой странице описаны основные шаги по созданию приложения для воспроизведения. Более подробную информацию можно найти в руководствах по Media3 ExoPlayer.
Начало работы
Для начала добавьте зависимость от модулей ExoPlayer, UI и Common в Jetpack Media3:
Kotlin
implementation("androidx.media3:media3-exoplayer:1.11.1")
implementation("androidx.media3:media3-ui:1.11.1")
implementation("androidx.media3:media3-common:1.11.1")
Яркий
implementation "androidx.media3:media3-exoplayer:1.11.1"
implementation "androidx.media3:media3-ui:1.11.1"
implementation "androidx.media3:media3-common:1.11.1"
В зависимости от ваших потребностей вам могут потребоваться дополнительные модули из Media3, например exoplayer-dash для воспроизведения потоков в формате DASH.
Замените 1.11.1 на нужную версию библиотеки. Последнюю версию можно найти в примечаниях к выпуску.
Создание медиапроигрывателя
В Media3 можно использовать встроенную реализацию интерфейса Player – ExoPlayer – или создать собственную.
Создание ExoPlayer
Проще всего создать экземпляр ExoPlayer следующим образом:
Kotlin
val player = ExoPlayer.Builder(context).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build();
Вы можете создать медиапроигрыватель в методе жизненного цикла onCreate() объекта Activity, Fragment или Service, в котором он находится.
Builder включает ряд параметров настройки, которые могут вас заинтересовать, например:
setAudioAttributes()для настройки обработки аудиофокуса.setHandleAudioBecomingNoisy(), чтобы настроить воспроизведение при отключении устройства вывода звука.setTrackSelector(), чтобы настроить выбор дорожки.
Media3 предоставляет компонент пользовательского интерфейса PlayerView, который можно включить в файл макета приложения. Этот компонент включает в себя PlayerControlView для элементов управления воспроизведением, SubtitleView для показа субтитров и Surface для воспроизведения видео.
Подготовка проигрывателя
Добавьте мультимедийные объекты в плейлист для воспроизведения с помощью таких методов, как setMediaItem() и addMediaItem(). Затем вызовите метод prepare(), чтобы начать загрузку медиаконтента и получить необходимые ресурсы.
Не выполняйте эти действия, пока приложение не будет открыто на переднем плане. Если проигрыватель находится в Activity или Fragment, это означает, что он подготавливается в методе жизненного цикла onStart() на уровне API 24 и выше или в методе жизненного цикла onResume() на уровне API 23 и ниже. Если проигрыватель находится в Service, вы можете подготовить его в onCreate(). Пример реализации методов жизненного цикла можно найти в практической работе по ExoPlayer.
Управление проигрывателем
После подготовки проигрывателя вы можете управлять воспроизведением, вызывая методы проигрывателя, например:
play()иpause()– начать и приостановить воспроизведение.seekTo()– перемотать до определенного момента в текущем мультимедийном объекте.seekToNextMediaItem()иseekToPreviousMediaItem()– навигация по плейлисту.
Компоненты пользовательского интерфейса, такие как PlayerView или PlayerControlView, будут обновляться соответствующим образом при привязке к проигрывателю.
Освободить игрока
Для воспроизведения могут требоваться ресурсы, доступ к которым ограничен, например декодеры видео. Поэтому важно вызывать метод release(), чтобы освобождать ресурсы, когда проигрыватель больше не нужен.
Если проигрыватель находится в Activity или Fragment, освободите его в методе жизненного цикла onStop() на уровне API 24 и выше или в методе onPause() на уровне API 23 и ниже. Если игрок находится в Service, вы можете выпустить его в onDestroy(). Пример реализации методов жизненного цикла можно найти в практической работе по ExoPlayer.
Управление воспроизведением с помощью сеанса мультимедиа
В Android сеансы мультимедиа обеспечивают стандартизированный способ взаимодействия с медиапроигрывателем за пределами процесса. Подключив сеанс воспроизведения к проигрывателю, вы сможете передавать информацию о воспроизведении медиаконтента на внешние устройства и получать от них команды, например для интеграции с системными элементами управления мультимедиа на мобильных устройствах и устройствах с большим экраном.
Чтобы использовать сеансы мультимедиа, добавьте зависимость от модуля Media3 Session:
Kotlin
implementation("androidx.media3:media3-session:1.11.1")
Яркий
implementation "androidx.media3:media3-session:1.11.1"
Как создать сеанс воспроизведения медиаконтента
Чтобы создать объект MediaSession, инициализируйте проигрыватель следующим образом:
Kotlin
val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();
Media3 автоматически синхронизирует состояние Player с состоянием MediaSession. Это работает с любой реализацией Player, включая ExoPlayer, CastPlayer и пользовательские реализации.
Предоставление управления другим клиентам
Клиентские приложения могут использовать контроллер мультимедиа для управления воспроизведением сеанса мультимедиа. Чтобы получать эти запросы, задайте объект callback при создании MediaSession.
Когда контроллер собирается подключиться к сеансу воспроизведения медиаконтента, вызывается метод onConnect(). Вы можете использовать предоставленный ControllerInfo, чтобы решить, принять или отклонить запрос. Пример можно посмотреть в демоприложении Media3 Session.
После подключения контроллер может отправлять команды воспроизведения в сеанс. Затем сеанс передает эти команды проигрывателю. Команды воспроизведения и плейлиста, определенные в интерфейсе Player, автоматически обрабатываются сеансом.
Другие методы обратного вызова позволяют обрабатывать, например, запросы на пользовательские команды воспроизведения и изменение плейлиста. Эти обратные вызовы также включают объект ControllerInfo, чтобы вы могли определять контроль доступа для каждого запроса.
Воспроизведение медиаконтента в фоновом режиме
Чтобы воспроизведение медиаконтента продолжалось, когда приложение неактивно, например чтобы пользователь мог слушать музыку, аудиокниги или подкасты, даже если приложение не открыто, Player и MediaSession должны быть инкапсулированы в активную службу. Для этого в Media3 есть интерфейс MediaSessionService.
Как внедрять правило MediaSessionService
Создайте класс, расширяющий MediaSessionService, и создайте экземпляр MediaSession в методе жизненного цикла onCreate().
Kotlin
class PlaybackService : MediaSessionService() {
private var mediaSession: MediaSession? = null
// Create your Player and MediaSession in the onCreate lifecycle event
override fun onCreate() {
super.onCreate()
val player = ExoPlayer.Builder(this).build()
mediaSession = MediaSession.Builder(this, player).build()
}
// Remember to release the player and media session in onDestroy
override fun onDestroy() {
mediaSession?.run {
player.release()
release()
mediaSession = null
}
super.onDestroy()
}
}
Java
public class PlaybackService extends MediaSessionService {
private MediaSession mediaSession = null;
@Override
public void onCreate() {
super.onCreate();
ExoPlayer player = new ExoPlayer.Builder(this).build();
mediaSession = new MediaSession.Builder(this, player).build();
}
@Override
public void onDestroy() {
mediaSession.getPlayer().release();
mediaSession.release();
mediaSession = null;
super.onDestroy();
}
}
В манифесте объявите класс Service с фильтром интентов MediaSessionService и запросите разрешение FOREGROUND_SERVICE на запуск активной службы:
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Наконец, в созданном классе переопределите метод onGetSession(), чтобы управлять доступом клиентов к сеансу воспроизведения. Чтобы принять запрос на подключение, верните MediaSession, а чтобы отклонить – null.
Kotlin
// This example always accepts the connection request
override fun onGetSession(
controllerInfo: MediaSession.ControllerInfo
): MediaSession? = mediaSession
Java
@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
// This example always accepts the connection request
return mediaSession;
}
Подключение к интерфейсу
Теперь, когда сеанс воспроизведения медиаконтента находится в Service отдельно от Activity или Fragment, где размещен интерфейс проигрывателя, вы можете использовать MediaController, чтобы связать их. В методе onStart() объекта Activity или Fragment с вашим интерфейсом создайте SessionToken для MediaSession, а затем используйте SessionToken, чтобы создать MediaController. Создание MediaController происходит асинхронно.
Kotlin
override fun onStart() {
val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
controllerFuture.addListener(
{
// Call controllerFuture.get() to retrieve the MediaController.
// MediaController implements the Player interface, so it can be
// attached to the PlayerView UI component.
playerView.setPlayer(controllerFuture.get())
},
MoreExecutors.directExecutor()
)
}
Java
@Override
public void onStart() {
SessionToken sessionToken =
new SessionToken(this, new ComponentName(this, PlaybackService.class));
ListenableFuture<MediaController> controllerFuture =
new MediaController.Builder(this, sessionToken).buildAsync();
controllerFuture.addListener(() -> {
// Call controllerFuture.get() to retrieve the MediaController.
// MediaController implements the Player interface, so it can be
// attached to the PlayerView UI component.
playerView.setPlayer(controllerFuture.get());
}, MoreExecutors.directExecutor())
}
MediaController реализует интерфейс Player, поэтому вы можете использовать те же методы, например play() и pause(), для управления воспроизведением. Как и в случае с другими компонентами, не забудьте освободить MediaController, когда он больше не нужен, в методе жизненного цикла onStop() объекта Activity, вызвав MediaController.releaseFuture().
Публикация уведомления
Активные службы должны показывать уведомление, пока они работают. MediaSessionService автоматически создаст для вас уведомление MediaStyle в виде MediaNotification. Чтобы создать собственное уведомление, создайте объект MediaNotification.Provider с помощью DefaultMediaNotificationProvider.Builder или создайте собственную реализацию интерфейса поставщика. Добавьте поставщика в MediaSession с помощью setMediaNotificationProvider.
Реклама библиотеки контента
MediaLibraryService расширяет возможности MediaSessionService, позволяя клиентским приложениям просматривать медиаконтент, предоставляемый вашим приложением. Клиентские приложения реализуют MediaBrowser для взаимодействия с вашим MediaLibraryService.
Реализация MediaLibraryService похожа на реализацию MediaSessionService, но в onGetSession() нужно возвращать MediaLibrarySession вместо MediaSession. По сравнению с MediaSession.Callback в MediaLibrarySession.Callback включены дополнительные методы, которые позволяют клиенту браузера перемещаться по контенту, предлагаемому вашим библиотечным сервисом.
Как и в случае с MediaSessionService, объявите MediaLibraryService в манифесте и запросите разрешение FOREGROUND_SERVICE, чтобы запустить активную службу.
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaLibraryService"/>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
В примере выше фильтр интентов включает как MediaLibraryService, так и устаревший MediaBrowserService (для обратной совместимости). Дополнительный фильтр интентов позволяет клиентским приложениям, использующим API MediaBrowserCompat, распознавать ваш Service.
MediaLibrarySession позволяет представить библиотеку контента в виде древовидной структуры с одним корневым элементом MediaItem. Каждый узел MediaItem в дереве может иметь любое количество дочерних узлов MediaItem. Вы можете показывать разные корневые элементы или разные деревья в зависимости от запроса клиентского приложения. Например, дерево, которое вы возвращаете клиенту, ищущему список рекомендуемых мультимедийных объектов, может содержать только корневой узел MediaItem и один уровень дочерних узлов MediaItem, тогда как дерево, которое вы возвращаете другому клиентскому приложению, может представлять более полную библиотеку контента.
Создание MediaLibrarySession
MediaLibrarySession расширяет API MediaSession, добавляя API для просмотра контента. По сравнению с обратным вызовом MediaSession обратный вызов MediaLibrarySession добавляет такие методы, как:
onGetLibraryRoot()– когда клиент запрашивает корневой каталогMediaItemдерева контента.onGetChildren()– когда клиент запрашивает дочерние элементыMediaItemв дереве контента.onGetSearchResult()– когда клиент запрашивает результаты поиска из дерева контента по заданному запросу.
Соответствующие методы обратного вызова будут включать объект LibraryParams с дополнительными сигналами о типе дерева контента, который интересует клиентское приложение.