Media3 / ExoPlayer для Kotlin: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

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                    

Рекомендаций