Media3 / ExoPlayer для Kotlin: полное руководство с примерами
1. Что это и зачем нужна
Media3 (ранее известный как ExoPlayer) — это современная, мощная и гибкая библиотека для воспроизведения аудио и видео на платформе Android, разработанная Google. Она является наследником устаревшего MediaPlayer из Android SDK и предлагает значительно более широкий функционал, лучшую производительность и контроль над процессом воспроизведения.
Зачем нужна Media3 / ExoPlayer? В отличие от стандартного MediaPlayer, который подходит только для простейших задач, ExoPlayer предоставляет разработчику полный контроль над медиа-конвейером. Она поддерживает множество форматов (DASH, HLS, SmoothStreaming, MP4, MP3, WebM и др.), адаптивное переключение битрейта (ABR), кастомизацию рендеринга, работу с субтитрами (включая WebVTT, TTML), DRM-защиту (Widevine, PlayReady) и многое другое. Библиотека активно развивается, оптимизируется и используется в таких приложениях, как YouTube, Google Photos и многих других популярных сервисах.
Для разработчиков на Kotlin Media3 является стандартом де-факто для создания любых медиа-приложений: от простого музыкального плеера до сложного видеоплеера с адаптивным стримингом и поддержкой DRM. Она позволяет писать чистый, модульный и тестируемый код, полностью контролируя жизненный цикл воспроизведения.
2. Установка
Media3 — это библиотека для Android, поэтому она устанавливается через систему сборки Gradle. Добавьте следующие зависимости в файл build.gradle.kts (или build.gradle) вашего модуля app:
// build.gradle.kts (Module :app)
dependencies {
// Базовая библиотека Media3 (обязательно)
implementation("androidx.media3:media3-exoplayer:1.4.1")
// UI-компоненты (PlayerView, PlayerControlView и т.д.)
implementation("androidx.media3:media3-ui:1.4.1")
// Для работы с HLS (HTTP Live Streaming)
implementation("androidx.media3:media3-exoplayer-hls:1.4.1")
// Для работы с DASH (Dynamic Adaptive Streaming over HTTP)
implementation("androidx.media3:media3-exoplayer-dash:1.4.1")
// Для работы с SmoothStreaming
implementation("androidx.media3:media3-exoplayer-rtsp:1.4.1")
// Для работы с субтитрами
implementation("androidx.media3:media3-exoplayer-ima:1.4.1")
// Для работы с DRM (Widevine)
implementation("androidx.media3:media3-exoplayer-drm:1.4.1")
// Для работы с сессиями (MediaSession) — обязательно для интеграции с системными элементами управления
implementation("androidx.media3:media3-session:1.4.1")
// Для работы с кастомными источниками данных (например, кэширование)
implementation("androidx.media3:media3-datasource:1.4.1")
// Для работы с кастомными кодеками (например, FFmpeg)
implementation("androidx.media3:media3-decoder:1.4.1")
// Для работы с кастомными эффектами (например, изменение скорости)
implementation("androidx.media3:media3-effect:1.4.1")
// Для работы с кастомными трансформациями (например, обрезка видео)
implementation("androidx.media3:media3-transformer:1.4.1")
}
Важно: Версии могут меняться. Актуальную версию всегда можно найти на официальной странице релизов.
После добавления зависимостей выполните синхронизацию Gradle (Sync Now) в Android Studio.
3. Быстрый старт — минимальный рабочий пример
Создадим простейший видеоплеер, который воспроизводит видео из интернета. Для этого нам понадобится PlayerView в XML-разметке и код в Activity/Fragment.
Шаг 1: Разметка (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.media3.ui.PlayerView
android:id="@+id/playerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:use_controller="true" />
</androidx.constraintlayout.widget.ConstraintLayout>
Шаг 2: Код Activity (MainActivity.kt)
import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
class MainActivity : AppCompatActivity() {
private var player: ExoPlayer? = null
private lateinit var playerView: PlayerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
playerView = findViewById(R.id.playerView)
// 1. Создаем плеер
player = ExoPlayer.Builder(this).build()
// 2. Привязываем плеер к PlayerView
playerView.player = player
// 3. Создаем MediaItem с URL видео
val videoUri = Uri.parse("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")
val mediaItem = MediaItem.fromUri(videoUri)
// 4. Задаем медиа-элемент плееру
player?.setMediaItem(mediaItem)
// 5. Готовим плеер к воспроизведению
player?.prepare()
// 6. Автоматически начинаем воспроизведение
player?.playWhenReady = true
}
override fun onStop() {
super.onStop()
// Освобождаем ресурсы плеера, когда Activity не видна
player?.release()
player = null
}
}
Этот код создает плеер, загружает видео Big Buck Bunny и начинает его воспроизведение. PlayerView автоматически отображает элементы управления (play/pause, перемотка, громкость).
4. Основные методы/функции/классы
4.1. ExoPlayer.Builder
Сигнатура: ExoPlayer.Builder(context: Context)
Что делает: Создает экземпляр плеера с настройками по умолчанию. Позволяет кастомизировать плеер через цепочку вызовов (например, задать рендереры, треки, обработчики ошибок).
Пример:
val player = ExoPlayer.Builder(this)
.setAudioAttributes(AudioAttributes.DEFAULT, true) // Разрешаем аудио в фоне
.setHandleAudioBecomingNoisy(true) // Останавливаем при снятии наушников
.build()
4.2. ExoPlayer.setMediaItem()
Сигнатура: fun setMediaItem(mediaItem: MediaItem)
Что делает: Устанавливает один медиа-элемент для воспроизведения. Можно также использовать setMediaItems() для плейлиста.
Пример:
val mediaItem = MediaItem.fromUri("https://example.com/audio.mp3")
player?.setMediaItem(mediaItem)
4.3. ExoPlayer.prepare()
Сигнатура: fun prepare()
Что делает: Начинает подготовку медиа-контента к воспроизведению. После вызова плеер переходит в состояние STATE_BUFFERING, а затем в STATE_READY.
Пример:
player?.apply {
setMediaItem(mediaItem)
prepare()
playWhenReady = true
}
4.4. ExoPlayer.play() и ExoPlayer.pause()
Сигнатура: fun play() / fun pause()
Что делает: Начинает или приостанавливает воспроизведение. В отличие от playWhenReady, эти методы не влияют на состояние подготовки.
Пример:
binding.playButton.setOnClickListener {
if (player?.isPlaying == true) {
player?.pause()
} else {
player?.play()
}
}
4.5. ExoPlayer.seekTo()
Сигнатура: fun seekTo(positionMs: Long)
Что делает: Перемещает позицию воспроизведения на указанное количество миллисекунд.
Пример:
// Перемотка на 1 минуту вперед
player?.seekTo(player?.currentPosition?.plus(60_000) ?: 0L)
4.6. ExoPlayer.addListener()
Сигнатура: fun addListener(listener: Player.Listener)
Что делает: Добавляет слушателя событий плеера (изменение состояния, ошибки, завершение трека и т.д.).
Пример:
player?.addListener(object : Player.Listener {
override fun onPlaybackStateChanged(playbackState: Int) {
when (playbackState) {
Player.STATE_READY -> Log.d("Player", "Готов к воспроизведению")
Player.STATE_ENDED -> Log.d("Player", "Воспроизведение завершено")
Player.STATE_BUFFERING -> Log.d("Player", "Буферизация...")
}
}
override fun onPlayerError(error: PlaybackException) {
Log.e("Player", "Ошибка: ${error.message}")
}
})
4.7. ExoPlayer.release()
Сигнатура: fun release()
Что делает: Освобождает все ресурсы плеера (декодеры, потоки, память). Обязательно вызывать в onStop() или onDestroy().
Пример:
override fun onStop() {
super.onStop()
player?.release()
player = null
}
4.8. MediaItem.Builder
Сигнатура: MediaItem.Builder()
Что делает: Позволяет создавать сложные медиа-элементы с дополнительными параметрами: субтитры, DRM, метаданные, настройки адаптивного стриминга.
Пример:
val mediaItem = MediaItem.Builder()
.setUri("https://example.com/video.mp4")
.setSubtitleConfigurations(
listOf(
MediaItem.SubtitleConfiguration.Builder(Uri.parse("https://example.com/subtitles.vtt"))
.setMimeType("text/vtt")
.setLanguage("ru")
.build()
)
)
.build()
4.9. PlayerView
Сигнатура: class PlayerView : ViewGroup
Что делает: UI-компонент для отображения видео и элементов управления. Может быть настроен через XML-атрибуты (например, use_controller, show_buffering).
Пример:
<androidx.media3.ui.PlayerView
android:id="@+id/playerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:use_controller="true"
app:show_buffering="when_playing"
app:resize_mode="fit" />
4.10. ExoPlayer.setPlaybackParameters()
Сигнатура: fun setPlaybackParameters(playbackParameters: PlaybackParameters)
Что делает: Изменяет скорость воспроизведения и/или высоту тона (pitch). Полезно для аудиокниг, подкастов или ускоренного просмотра.
Пример:
// Ускорение в 2 раза без изменения тона
player?.setPlaybackParameters(PlaybackParameters(2.0f, 1.0f))
5. Реальный практический пример (100-200 строк кода)
Создадим полноценный аудиоплеер с плейлистом, управлением через системные кнопки (MediaSession) и отображением текущего трека.
import android.net.Uri
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.media3.common.AudioAttributes
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.session.MediaSession
import androidx.media3.session.MediaSessionService
class AudioPlayerActivity : AppCompatActivity() {
private var player: ExoPlayer? = null
private var mediaSession: MediaSession? = null
private lateinit var trackTitle: TextView
private lateinit var btnPlayPause: Button
private lateinit var btnNext: Button
private lateinit var btnPrev: Button
// Плейлист
private val playlist = listOf(
"https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3",
"https://www.s