Інтеграція ExoPlayer для відтворення відео в Android-застосунку
MediaPlayer зі стандартної бібліотеки Android справляється з локальними файлами та простими HTTP-посиланнями. Як тільки з'являється HLS-стрім, DASH-маніфест, DRM-захист через Widevine, або потрібна перемотка в потоці без переозначення з початку — MediaPlayer закінчується і починається ExoPlayer.
ExoPlayer (з Media3 — androidx.media3:media3-exoplayer) — бібліотека Google для медіавідтворення на Android. Вона використовується у YouTube, Google TV та більшості потокових застосунків на платформі.
Що конкретно інтегруємо
Базова інтеграція: ExoPlayer + PlayerView в layout, завантаження MediaItem за URL, управління відтворенням через Player.Listener. Це займає кілька годин.
Реальні завдання складніше. Адаптивний стрімінг (HLS/DASH): HlsMediaSource або DashMediaSource з DefaultDataSource.Factory. ExoPlayer автоматично вибирає якість на основі AdaptiveTrackSelection — але потрібно правильно налаштувати DefaultBandwidthMeter та DefaultLoadControl з параметрами буферизації (minBufferMs, maxBufferMs, bufferForPlaybackMs). Неправильні параметри — постійні паузи на буферизацію навіть при швидкому інтернеті.
DRM через Widevine: DefaultDrmSessionManager з HttpMediaDrmCallback, який звертається до сервера ліцензій. Сертифікат Widevine L1 працює лише на пристроях з апаратним DRM-середовищем (TEE). L3 — програмний, на всіх пристроях, але якість обмежена. У маніфесті потрібен android:requestLegacyExternalStorage для деяких старих пристроїв з особливостями файлової системи.
Picture-in-Picture: при переході в фоновий режим ExoPlayer має продовжувати відтворення. Activity перемикається в PiP-режим через enterPictureInPictureMode() з PictureInPictureParams. Ключовий момент — Player не привязаний до lifecycle Activity прямо, інакше він зупиняється при onStop(). Рішення: ExoPlayer в MediaSessionService або хоча б правильний lifecycle-aware компонент.
Типові проблеми
ExoPlayer на PlayerView всередину RecyclerView — класична біль. При швидкому скролі виникає стан, коли кілька PlayerView одночасно намагаються програти медіа. Потрібна логіка «один активний плеєр» з паузою попередньої при появленні наступної. RecyclerView.OnScrollListener + LinearLayoutManager.findFirstCompletelyVisibleItemPosition() — стандартний підхід.
Audio-фокус: якщо застосунок не запитує AudioFocus та не реагує на його втрату, музичний плеєр користувача не приостановиться. AudioFocusRequest з OnAudioFocusChangeListener — обов'язкова частина будь-якого відеоплеєра.
Переход між екранами без переривання відтворення: ExoPlayer потрібно зберігати вище lifecycle екрана — у ViewModel або сингелтоні сервісу. PlayerView.setPlayer(null) при виході з екрана, PlayerView.setPlayer(player) при поверненні. Без цього буде чорний екран або подвійний звук.
Версіонування: Media3 1.3.x — актуальна гілка на момент написання. Старий com.google.android.exoplayer2 deprecated, перехід на androidx.media3 — частина роботи при інтеграції в існуючий проект.
Строк інтеграції: 2–3 дні для базового плеєра з HLS та управлінням відтворенням. Widevine DRM, PiP, плеєр у RecyclerView — кожне додає 1–2 дні. Вартість розраховується індивідуально.







