
VK Видео — один из крупнейших видеосервисов в России. В декабре 2025 года его ежемесячная аудитория достигла 81,5 млн пользователей. А в первую неделю января 2026 года среднесуточная аудитория VK Видео превысила 42 млн человек (без учёта просмотров на Smart TV и встраиваемых плееров, по данным Mediascope).
Часть аудитории пользуется сервисом на устройствах Android. Для работы с видео на Android у VK есть единое решение — SDK OneVideo Player. Оно основано на библиотеке Media3 и содержит много улучшений и доработок в части проигрывания видео. Одна из таких оптимизаций — адаптация видео под viewport.
Меня зовут Егор Баженов. Я Android-разработчик в команде видеоплатформы VK. В этой статье я расскажу, как мы внедрили адаптацию под viewport в нашу библиотеку и каких результатов это позволило достичь.
Начнём с основ: viewport и разные разрешения видео
Viewport
Viewport (вьюпорт) — часть сайта или приложения, которую пользователь видит сразу, без прокрутки. В контексте видео вьюпорт — область видимости видео на экране устройства.
На разных устройствах вьюпорты отличаются, так как область видимости зависит от реальных размеров смартфонов — а они у каждой модели свои. На экранах разных устройств элементы будут расположены по-разному.

Качество видео
Эта метрика определяет, насколько хорошо и чётко выглядит картинка на экране.
Рассмотрим все качества, представленные у нас в SDK, и возьмём стандарты для соотношения сторон 16:9.
Качество видео | Соотношение пикселей (ширина × высота) | Количество пикселей |
144p | 256 × 144 | 36 864 |
240p | 426 × 240 | 102 240 |
360p | 640 × 360 | 230 400 |
480p (SD) | 854 × 480 | 409 920 |
480p (HD) | 1 280 × 720 | 921 600 |
1080p (Full HD) | 1 920 × 1 080 | 2 073 600 |
1440p (Quad HD) | 2 560 × 1 440 | 3 686 400 |
2160p (4k Ultra HD) | 3 840 × 2 160 | 8 294 400 |
4320p (8k Ultra HD) | 7 680 × 4 320 | 33 177 600 |
Видим, что с ростом качества видео пикселей становится кратно больше. Это означает, что при переходе, например, с низкого разрешения (SD) на высокое (HD, 4K и выше) объём данных для передачи и отображения одного кадра увеличивается в несколько раз. Вместе с этим растёт и объём потребляемого трафика.
Рост связан с тем, что для хранения и передачи каждого пикселя требуется определённое количество бит. Чем больше пикселей, тем больше данных нужно передать за единицу времени. Это особенно важно при потоковой передаче видео. Если скорость интернета ограничена, пользователи могут столкнуться с задержками или ухудшением качества, так как пропускная способность сети снизилась.
Связь качества видео и вьюпорта
Если видео воспроизводится в автокачестве, плеер (а точнее, адаптивный алгоритм выбора трека) по умолчанию в первую очередь ориентируется на пропускную способность сети. Это помогает решить, какую дорожку выбрать для проигрывания.
Но это не совсем верный подход. Параметры пропускной способности действительно важны, но нужно учитывать и размеры вьюпорта, где воспроизводится видео.
Чтобы было понятнее, рассмотрим примеры. Здесь в каждой таблице — один и тот же кадр из видео в разных качествах и с разным вьюпортом.


В полноэкранном режиме разница между качествами хорошо заметна, так как вьюпорт видео соответствует вьюпорту телефона. А вот для дискавера, когда ролик смотрят в вертикальном режиме без фулскрина, все качества выше 720p визуально почти одинаковы. Вьюпорт видео не может целиком передать изменение качества — он для этого слишком маленький. Более того, если вьюпорт меньше, чем качество видео, картинки в разном разрешении для человека выглядят практически одинаково.
Это значит, что при выборе трека оптимально и рационально ориентироваться не только на пропускную способность, но и на размеры вьюпорта плеера. Так можно оптимизировать потребление трафика пользователя и улучшить стабильность видео.
От основ к нашей реализации
Теперь, когда мы разобрались с вьюпортом, качеством видео и связью между ними, перейдём к главному — деталям нашей реализации.
Она состоит из двух основных частей:
передачи вьюпорта плееру;
выбора трека с учётом размера вьюпорта.
Передача вьюпорта плееру
Передача вьюпорта у нас реализована так:
при создании плеера мы также передаём view (вью) для воспроизведения — это может быть
TextureViewилиSurfaceView;в этот момент создаётся класс
OneVideoSurfaceHolderна базе переданной вью. Внутри будет храниться значение вьюпорта на основании её размеров.
Класс OneVideoSurfaceHolder выглядит так:
package one.video.player import android.util.Size import android.view.SurfaceView import android.view.TextureView import android.view.View class OneVideoViewHolder { constructor(surfaceView: SurfaceView) { attachView(surfaceView) } constructor(textureView: TextureView) { attachView(textureView) } @Volatile var viewportSize: Size? = null private set private fun attachView(view: View) { //Size 0x0 equals null viewportSize val setNonEmptyViewport: (View) -> Unit = { v -> viewportSize = if (v.width == 0 || v.height == 0) null else Size(v.width, v.height) } setNonEmptyViewport(view) view.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ -> setNonEmptyViewport(v) } } }
Выбор трека с учётом размера вьюпорта
Теперь разберём, как устроен учёт размера вьюпорта при выборе треков.
У нас есть своя реализация интерфейса exoplayer.trackselection.TrackSelection на базе AdaptiveTrackSelection, которая называется OneVideoTrackSelection. Она обновляет selectedIndex, отвечающий за выбор трека, на основании значений вьюпорта, которые мы передали в конструкторе.
На уровне кода это выглядит так:
Код
@OptIn(UnstableApi::class) package one.video.exo.trackselection import android.util.Size import androidx.annotation.OptIn import androidx.media3.common.Format import androidx.media3.common.TrackGroup import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.source.chunk.MediaChunk import androidx.media3.exoplayer.source.chunk.MediaChunkIterator import androidx.media3.exoplayer.trackselection.AdaptiveTrackSelection import androidx.media3.exoplayer.upstream.BandwidthMeter class OneVideoTrackSelection1( group: TrackGroup, tracks: IntArray, bandwidthMeter: BandwidthMeter, private val viewportSizeProvider: () -> Size?, ) : AdaptiveTrackSelection( group, tracks, bandwidthMeter, ) { private val videoTracks: List<Format> by lazy { List(length) { getFormat(it) } } override fun updateSelectedTrack( playbackPositionUs: Long, bufferedDurationUs: Long, availableDurationUs: Long, queue: List<MediaChunk>, mediaChunkIterators: Array<out MediaChunkIterator> ) { super.updateSelectedTrack( playbackPositionUs, bufferedDurationUs, availableDurationUs, queue, mediaChunkIterators ) updateSelectedIndexByViewportSize() } private fun updateSelectedIndexByViewportSize() { val viewportSize = viewportSizeProvider() ?: return val needToAdaptToViewport = !fitsFully(viewportSize, videoTracks[selectedIndex]) if (needToAdaptToViewport) { videoTracks.forEachIndexed { index, format -> if (fitsFully(viewportSize, format)) { selectedIndex = index return } } } } private fun fitsFully(viewportSize: Size, format: Format) = format.width <= viewportSize.width && format.height <= viewportSize.height }
Особенности интеграции
При интеграции этой реализации нужно учесть, что:
Она работает только для автокачества. Именно в этом случае алгоритм решает за пользователя, какой трек сейчас лучше выбрать для проигрывания.
VideoTracks должны быть отсортированы по размеру фрейма. Это нужно проверить отдельно, так как дефолтная реализация завязана на bitrate. У нас сортировку треков по размеру фрейма гарантирует бэкенд.
Код выше показывает самую базовую имплементацию. В реальности у нас много дополнительных параметров для настройки плеера. Но они напрямую не связаны с адаптацией под вьюпорт, поэтому мы их опустили в листингах кода.
Наши результаты адаптации видео под вьюпорт
Чтобы оценить эффективность интеграции изменений, перед выкаткой в прод мы провели A/B-эксперименты. По их результатам:
количество зависаний снизилось на 13%;
приложение стало потреблять меньше трафика;
время показа первого кадра ускорилось на 2%;
FTR и ANR приложения сократились.
Одновременно с этим выросли метрики стартов и досмотров видео.
Краткие выводы
Наш опыт показывает, что адаптация видео под вьюпорт помогает эффективно оптимизировать трафик и бороться с зависаниями. Она позволяет улучшить и технические, и продуктовые показатели. Поэтому при работе с видео важно учитывать не только пропускную способность канала, но и размер вью у видео — так получится добиться стабильного и быстрого воспроизведения.
Мы не стремились работать с узкоспециализированными подходами. Примеры и код из статьи можно адаптировать для оптимизации любых решений на базе Media3.
