Интеграция 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. Это занимает несколько часов.
Реальные задачи сложнее. Adaptive streaming (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() — стандартный подход.
Аудио-фокус: если приложение не запрашивает 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 дня. Стоимость рассчитывается индивидуально.







