ExoPlayer е библиотека с отворен код за медиен плейър за Android, разработена от Google като алтернатива на вградения MediaPlayer. ExoPlayer осигурява възпроизвеждане на аудио и видео в различни формати, включително DASH, HLS, SmoothStreaming и обикновени медийни файлове, чрез единен API с поддръжка за адаптивно стрийминг. Според Google I/O (2024), ExoPlayer се използва в над 60 000 Android приложения, включително YouTube, Google TV и Netflix.
Основни точки
ExoPlayer е библиотека за мултимедийно възпроизвеждане на Android с отворен код (Apache 2.0), чиято първа версия беше пусната от Google през 2014 г. За разлика от вградения MediaPlayer, който е тънък слой върху системния медиен кодек (MediaCodec), ExoPlayer е имплементиран изцяло на ниво Java/Kotlin, което дава на разработчиците пълен контрол над всеки етап от възпроизвеждането.
Архитектурно, ExoPlayer е изграден на модулен принцип: всеки аспект на възпроизвеждането се имплементира от отделен компонент. Основният модул (exoplayer-core) съдържа плейъра, рендери, селектори на писти и управление на буфера. Допълнителните модули (exoplayer-dash, exoplayer-hls, exoplayer-rtsp, exoplayer-smoothstreaming) добавят поддръжка за съответните стрийминг протоколи. Тази архитектура позволява на приложенията да свързват само необходимите компоненти, минимизирайки размера на APK.
ExoPlayer 2 (текущата основна версия) беше пусната през 2018 г. и донесе основен ремонт на API: въвеждане на Player Interface, поддръжка на Compose чрез AndroidX Media3, подобрено обработване на грешки и адаптивни алгоритми за избор на писти. От версия 2.19 (2024) ExoPlayer стана част от библиотеката AndroidX Media, което опрости интеграцията и актуализирането чрез стандартния мениджър на зависимости за Android.
В основата на ExoPlayer стои компонентът ExoPlayerImpl, който управлява жизнения цикъл на възпроизвеждането. Той съдържа MediaSource (медиен източник), TrackSelector (избор на писти), LoadControl (управление на буферирането) и списък с Renderer (рендери за видео, аудио, субтитри и метаданни). Всеки компонент може да бъде заменен със собствена имплементация чрез Builder или фабрични методи.
За рендериране на видео ExoPlayer използва SurfaceView, TextureView или SphericalSurfaceView (за 360-градусово видео). Препоръчителният вариант е SurfaceView, тъй като осигурява хардуерно ускорение и минимална консумация на енергия. За Jetpack Compose библиотеката AndroidX Media3 предоставя компонента `AndroidView`, който обвива SurfaceView, и експерименталния `Media3Compose` с родна Compose интеграция.
ExoPlayer предоставя широк набор от функции, които надхвърлят обикновения медиен плейър. Основните възможности включват поддръжка за всички популярни стрийминг протоколи, гъвкава DRM система, адаптивно превключване на битрейт (ABR) и инструменти за аналитика на възпроизвеждането.
| Функция | Описание | Модул |
|---|---|---|
| DASH | Адаптивен стрийминг по стандарт MPEG | exoplayer-dash |
| HLS | HTTP Live Streaming от Apple | exoplayer-hls |
| SmoothStreaming | Адаптивен стрийминг от Microsoft | exoplayer-smoothstreaming |
| RTSP | Real-Time Streaming Protocol за на живо | exoplayer-rtsp |
| Widevine | DRM защита на съдържание | exoplayer-drm |
| Concatenation | Слепване на няколко медийни файла | exoplayer-core |
| Ads | IMA SDK интеграция за реклами | exoplayer-ima |
ABR (Adaptive Bitrate) в ExoPlayer е имплементиран чрез интерфейса AdaptiveTrackSelection. По подразбиране се използва AdaptiveTrackSelectionFactory, който създава DefaultAdaptiveTrackSelection — балансиран алгоритъм, който взема предвид честотната лента на мрежата и размера на буфера. За специфични сценарии могат да се свържат алтернативни имплементации: BOLA (минимизиране на превключванията), RandomAdaptiveTrackSelection (тестване) или собствена имплементация чрез интерфейса TrackSelection.
BandwidthMeter е друг ключов компонент, отговорен за измерване на честотната лента на мрежата. По подразбиране се използва DefaultBandwidthMeter, който събира статистика от всички HTTP заявки на плейъра. Разработчикът може да го разшири, за да вземе предвид допълнителни фактори: тип на мрежата (Wi-Fi, 4G, 5G), сигнал на клетъчната кула или цена на трафика за потребителя.
ExoPlayer поддържа Widevine (L1, L3), PlayReady и ClearKey DRM. Получаването на лиценз се конфигурира чрез DrmSessionManager, който взаимодейства с лицензионния сървър. За премиум видео (4K HDR) се изисква хардуерна поддръжка на Widevine L1, която осигурява защита на ниво чип и предотвратява заснемане на екрана. Изборът на DRM система зависи от целевата платформа: Widevine се използва на Android и Chromecast, PlayReady на Xbox и Windows, FairPlay на iOS и Apple TV. ExoPlayer автоматично определя наличната DRM система въз основа на MPD файла и не изисква ръчна конфигурация за основни сценарии.
Свързването на ExoPlayer към Android проект се извършва чрез Gradle зависимости. От версия 2.19 ExoPlayer е част от AndroidX Media (androidx.media3), което осигурява съвместимост с Jetpack Compose и други AndroidX компоненти. Нека разгледаме стъпка по стъпка интеграцията в Kotlin.
Добавете модулите на ExoPlayer във файла build.gradle (Module). Минималният набор включва core, ui и модула за необходимия стрийминг протокол. За възпроизвеждане на DASH съдържание е необходим exoplayer-dash, за HLS — exoplayer-hls.
dependencies {
implementation "androidx.media3:media3-exoplayer:1.5.0"
implementation "androidx.media3:media3-ui:1.5.0"
implementation "androidx.media3:media3-exoplayer-dash:1.5.0"
implementation "androidx.media3:media3-exoplayer-hls:1.5.0"
}
Създаване и конфигуриране на плейъра в Activity или Fragment. ExoPlayer използва PlayerView (или StyledPlayerView за персонализация) за показване на видео и вградени контроли. Минималната конфигурация включва създаване на плейъра чрез ExoPlayer.Builder и предаване на MediaItem с URI на източника.
class PlayerActivity : AppCompatActivity() {
private var player: ExoPlayer? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_player)
player = ExoPlayer.Builder(this).build()
val playerView = findViewById<StyledPlayerView>(R.id.playerView)
playerView.player = player
val mediaItem = MediaItem
.Builder()
.setUri("https://example.com/video.mp4")
.build()
player?.setMediaItem(mediaItem)
player?.prepare()
}
override fun onDestroy() {
player?.release()
super.onDestroy()
}
}
За производствени приложения е необходима обработка на възможните грешки при възпроизвеждане: мрежови повреди, неподдържани формати, грешки при DRM лицензиране. ExoPlayer предоставя интерфейса Player.Listener с методи onPlayerError, onPlaybackStateChanged и onPlayWhenReadyChanged. Препоръчва се показване на информативни съобщения за грешки с възможност за повторение.
Изборът между ExoPlayer и вградения MediaPlayer е често срещан въпрос при разработката на Android приложения. MediaPlayer е част от Android SDK от версия 1.0, докато ExoPlayer е библиотека, която изисква свързване чрез Gradle. Нека разгледаме разликите им по ключови критерии.
| Критерий | ExoPlayer | MediaPlayer |
|---|---|---|
| Архитектура | Модулна, изцяло на Java/Kotlin | Слой върху системни C++ компоненти |
| Стрийминг | DASH, HLS, SmoothStreaming, RTSP | Само локални файлове и HLS (Android 9+) |
| Персонализация | Пълна (всеки компонент заменяем) | Минимална (само параметри) |
| Актуализации | Чрез Google Play или APK (независимо от ОС) | Само чрез актуализация на Android (OTA) |
| Размер на APK | +1-3 MB (в зависимост от модулите) | 0 (част от системата) |
| Min SDK | API 16+ (Android 4.1) | API 1+ |
| DRM | Widevine, PlayReady, ClearKey | Widevine (ограничено) |
ExoPlayer печели в сценарии, изискващи стрийминг (DASH, HLS), персонализация на UI, поддръжка на DRM и независимост от версията на Android. MediaPlayer е подходящ за прости приложения, които възпроизвеждат локални медийни файлове, където размерът на APK е критичен и няма изисквания за адаптивен стрийминг. За модерни приложения със съдържание от мрежата ExoPlayer е безспорният избор.
Изключение — приложения с минимално SDK под API 16 (Android 4.1), които практически не съществуват на пазара. За всички приложения с targetSdk 33+ и минимално SDK 21+, ExoPlayer не само се предпочита, но и е необходим за поддръжка на съвременни формати като AV1 и Dolby Vision чрез съответните разширения.
За производствени приложения основната интеграция на ExoPlayer често изисква допълнителна настройка: управление на кеша, оптимизация на буферирането за мобилни мрежи, интеграция с аналитика и персонализация на избора на аудио писти. Нека разгледаме разширени сценарии на използване.
CacheDataSourceFactory позволява кеширане на изтеглени сегменти в локално хранилище, което спестява трафик при повторно гледане и осигурява офлайн възпроизвеждане. ExoPlayer използва SimpleCache от библиотеката exoplayer-cas — файлово кеширане с поддръжка на LRU евакуация и ограничения на размера.
val cache = SimpleCache(
cacheDir,
LeastRecentlyUsedCacheEvictor(50 * 1024 * 1024),
AppDatabaseProvider(this)
)
val cacheDataSourceFactory = CacheDataSource
.Factory()
.setCache(cache)
val player = ExoPlayer.Builder(this)
.setMediaSourceFactory(
DefaultMediaSourceFactory(cacheDataSourceFactory)
)
.build()
В мобилните приложения обработката на превключване между Wi-Fi и клетъчна мрежа е критична. ExoPlayer предоставя ConnectivityManager за проследяване на състоянието на мрежата. При загуба на връзка се препоръчва да не спирате плейъра, а да го превключите в състояние PAUSED със запазване на позицията. След възстановяване на връзката продължете възпроизвеждането с презареждане на сегментите.
С пускането на AndroidX Media3 библиотеката получи експериментална поддръжка за Jetpack Compose. Компонентът `AndroidView` обвива SurfaceView или PlayerView, осигурявайки съвместимост с Compose йерархията. За по-дълбока интеграция се използва библиотеката `androidx.media3:media3-ui-compose`, която предоставя Compose-съвместими контроли за плейъра.
@Composable
fun VideoPlayer(uri: String) {
val context = LocalContext.current
val player = remember {
ExoPlayer.Builder(context).build()
.also { it.setMediaItem(MediaItem.fromUri(uri)) }
}
DisposableEffect(key = null) {
player.prepare()
onDispose { player.release() }
}
AndroidView(
factory = { StyledPlayerView(context).also { it.player = player } }
)
}
Често задавани въпроси
ExoPlayer поддържа Android API 16+ (Android 4.1 Jelly Bean). Въпреки това, за DASH и HLS с fMP4 сегменти се препоръчва API 21+ (Android 5.0 Lollipop). DRM модулите (Widevine) изискват API 19+, а хардуерното декодиране на HEVC е достъпно от API 21.
Да, ExoPlayer е отличен за аудио. За възпроизвеждане само на звук не е необходим SurfaceView или PlayerView. ExoPlayer автоматично ще избере аудио рендер и ще работи на заден план. За фоново възпроизвеждане се препоръчва използването на MediaSessionService за интеграция със системните контроли.
TrackSelector в ExoPlayer управлява избора на аудио писти. За превключване използвайте player.getCurrentTracks().groups и player.setTrackSelectionParameters() с посочване на предпочитания аудио език. За DASH и HLS потоци с множество аудио писти превключването става без прекъсване на възпроизвеждането.
Да, ExoPlayer поддържа вградени (в контейнер MP4, WebM) и външни субтитри във формати TTML, SRT, VTT и CEA-608. За външни субтитри се използва SingleSampleMediaSource или MergingMediaSource за комбиниране с видео потока. Показването на стилизирани субтитри се конфигурира чрез CaptionStyleCompat.
За нисколатентен стрийминг конфигурирайте LoadControl с по-малки целеви стойности на буфера: използвайте DefaultLoadControl.Builder().setTargetBufferBytes(1024 * 512).setBufferDurationsMs(500, 2000, 500, 1000) за HLS LL и DASH LL. Също така задайте minPlaybackSpeed = 1.02 за ускорено пълнене на буфера при изоставане от предаването на живо.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също